#ifndef utils_hh_INCLUDED #define utils_hh_INCLUDED #include "assert.hh" #include "exception.hh" #include #include #include #include namespace Kakoune { // *** Singleton *** // // Singleton helper class, every singleton type T should inherit // from Singleton to provide a consistent interface. template class Singleton { public: Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; static T& instance() { kak_assert (ms_instance); return *ms_instance; } static void delete_instance() { delete ms_instance; ms_instance = nullptr; } static bool has_instance() { return ms_instance != nullptr; } protected: Singleton() { kak_assert(not ms_instance); ms_instance = static_cast(this); } ~Singleton() { kak_assert(ms_instance == this); ms_instance = nullptr; } private: static T* ms_instance; }; template T* Singleton::ms_instance = nullptr; // *** safe_ptr: objects that assert nobody references them when they die *** template class safe_ptr { public: safe_ptr() : m_ptr(nullptr) {} explicit safe_ptr(T* ptr) : m_ptr(ptr) { #ifdef KAK_DEBUG if (m_ptr) m_ptr->inc_safe_count(); #endif } safe_ptr(const safe_ptr& other) : safe_ptr(other.m_ptr) {} safe_ptr(safe_ptr&& other) : m_ptr(other.m_ptr) { other.m_ptr = nullptr; } ~safe_ptr() { #ifdef KAK_DEBUG if (m_ptr) m_ptr->dec_safe_count(); #endif } safe_ptr& operator=(const safe_ptr& other) { #ifdef KAK_DEBUG if (m_ptr != other.m_ptr) { if (m_ptr) m_ptr->dec_safe_count(); if (other.m_ptr) other.m_ptr->inc_safe_count(); } #endif m_ptr = other.m_ptr; return *this; } safe_ptr& operator=(safe_ptr&& other) { #ifdef KAK_DEBUG if (m_ptr) m_ptr->dec_safe_count(); #endif m_ptr = other.m_ptr; other.m_ptr = nullptr; return *this; } void reset(T* ptr) { *this = safe_ptr(ptr); } bool operator== (const safe_ptr& other) const { return m_ptr == other.m_ptr; } bool operator!= (const safe_ptr& other) const { return m_ptr != other.m_ptr; } bool operator== (T* ptr) const { return m_ptr == ptr; } bool operator!= (T* ptr) const { return m_ptr != ptr; } T& operator* () const { return *m_ptr; } T* operator-> () const { return m_ptr; } T* get() const { return m_ptr; } explicit operator bool() const { return m_ptr; } private: T* m_ptr; }; class SafeCountable { public: #ifdef KAK_DEBUG SafeCountable() : m_count(0) {} ~SafeCountable() { kak_assert(m_count == 0); } void inc_safe_count() const { ++m_count; } void dec_safe_count() const { --m_count; kak_assert(m_count >= 0); } private: mutable int m_count; #endif }; // *** Containers helpers *** template struct ReversedContainer { ReversedContainer(Container& container) : container(container) {} Container& container; decltype(container.rbegin()) begin() { return container.rbegin(); } decltype(container.rend()) end() { return container.rend(); } }; template ReversedContainer reversed(Container&& container) { return ReversedContainer(container); } template auto find(Container&& container, const T& value) -> decltype(container.begin()) { return std::find(container.begin(), container.end(), value); } template auto find_if(Container&& container, T op) -> decltype(container.begin()) { return std::find_if(container.begin(), container.end(), op); } template bool contains(Container&& container, const T& value) { return find(container, value) != container.end(); } template bool contains(const std::unordered_set& container, const T2& value) { return container.find(value) != container.end(); } // *** On scope end *** // // on_scope_end provides a way to register some code to be // executed when current scope closes. // // usage: // auto cleaner = on_scope_end([]() { ... }); // // This permits to cleanup c-style resources without implementing // a wrapping class template class OnScopeEnd { public: OnScopeEnd(T func) : m_func(std::move(func)) {} ~OnScopeEnd() { m_func(); } private: T m_func; }; template OnScopeEnd on_scope_end(T t) { return OnScopeEnd(t); } // *** Misc helper functions *** template bool operator== (const std::unique_ptr& lhs, T* rhs) { return lhs.get() == rhs; } inline String escape(const String& name) { static Regex ex{"([ \\t;])"}; return boost::regex_replace(name, ex, R"(\\\1)"); } template const T& clamp(const T& val, const T& min, const T& max) { return (val < min ? min : (val > max ? max : val)); } template bool is_in_range(const T& val, const T& min, const T& max) { return min <= val and val <= max; } // *** AutoRegister: RAII handling of value semantics registering classes *** template class AutoRegister { public: AutoRegister(Registry& registry) : m_registry(®istry) { RegisterFuncs::insert(*m_registry, effective_this()); } AutoRegister(const AutoRegister& other) : m_registry(other.m_registry) { RegisterFuncs::insert(*m_registry, effective_this()); } AutoRegister(AutoRegister&& other) : m_registry(other.m_registry) { RegisterFuncs::insert(*m_registry, effective_this()); } ~AutoRegister() { RegisterFuncs::remove(*m_registry, effective_this()); } AutoRegister& operator=(const AutoRegister& other) { if (m_registry != other.m_registry) { RegisterFuncs::remove(*m_registry, effective_this()); m_registry = other.m_registry; RegisterFuncs::insert(*m_registry, effective_this()); } return *this; } AutoRegister& operator=(AutoRegister&& other) { if (m_registry != other.m_registry) { RegisterFuncs::remove(*m_registry, effective_this()); m_registry = other.m_registry; RegisterFuncs::insert(*m_registry, effective_this()); } return *this; } Registry& registry() const { return *m_registry; } private: EffectiveType& effective_this() { return static_cast(*this); } Registry* m_registry; }; } #endif // utils_hh_INCLUDED