#ifndef utils_hh_INCLUDED #define utils_hh_INCLUDED #include "assert.hh" #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 *static_cast(ms_instance); } static bool has_instance() { return ms_instance != nullptr; } protected: Singleton() { kak_assert(ms_instance == nullptr); ms_instance = this; } ~Singleton() { kak_assert(ms_instance == this); ms_instance = nullptr; } private: static Singleton* ms_instance; }; template Singleton* Singleton::ms_instance = nullptr; // *** 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 [[nodiscard]] OnScopeEnd { public: [[gnu::always_inline]] OnScopeEnd(T func) : m_valid{true}, m_func{std::move(func)} {} [[gnu::always_inline]] OnScopeEnd(OnScopeEnd&& other) : m_valid{other.m_valid}, m_func{std::move(other.m_func)} { other.m_valid = false; } [[gnu::always_inline]] ~OnScopeEnd() noexcept(noexcept(std::declval()())) { if (m_valid) m_func(); } private: bool m_valid; T m_func; }; template OnScopeEnd on_scope_end(T t) { return OnScopeEnd{std::move(t)}; } // bool that can be set (to true) multiple times, and will // be false only when unset the same time; struct NestedBool { void set() { m_count++; } void unset() { kak_assert(m_count > 0); m_count--; } operator bool() const { return m_count > 0; } private: int m_count = 0; }; struct ScopedSetBool { ScopedSetBool(NestedBool& nested_bool, bool condition = true) : m_nested_bool(nested_bool), m_condition(condition) { if (m_condition) m_nested_bool.set(); } ScopedSetBool(ScopedSetBool&& other) : m_nested_bool(other.m_nested_bool), m_condition(other.m_condition) { other.m_condition = false; } ~ScopedSetBool() { if (m_condition) m_nested_bool.unset(); } private: NestedBool& m_nested_bool; bool m_condition; }; // *** Misc helper functions *** template bool operator== (const std::unique_ptr& lhs, T* rhs) { return lhs.get() == rhs; } template const T& clamp(const T& val, const T& min, const T& max) { return (val < min ? min : (val > max ? max : val)); } template bool skip_while(Iterator& it, const EndIterator& end, T condition) { while (it != end and condition(*it)) ++it; return it != end; } template bool skip_while_reverse(Iterator& it, const BeginIterator& begin, T condition) { while (it != begin and condition(*it)) --it; return condition(*it); } template auto to_underlying(E value) { return static_cast>(value); } template class FunctionRef; template concept ConvertibleTo = std::is_convertible_v; template class FunctionRef { public: FunctionRef() : m_target{nullptr}, m_invoker{[](void* target, Args... args) { if constexpr (!std::is_same_v) return Res{}; }} {} template requires requires (Target t, Args... a) { requires not std::is_same_v>; { t(a...) } -> ConvertibleTo; } FunctionRef(Target&& target) : m_target{&target}, m_invoker{[](void* target, Args... args) { return (*reinterpret_cast(target))(static_cast(args)...); }} {} Res operator()(Args... args) const { return m_invoker(m_target, static_cast(args)...); } private: using Invoker = Res (void*, Args...); void* m_target; Invoker* m_invoker; }; template struct Overload : Funcs... { using Funcs::operator()...; }; template auto overload(Funcs&&... funcs) { return Overload...>{std::forward(funcs)...}; } } #endif // utils_hh_INCLUDED