diff --git a/CMakeLists.txt b/CMakeLists.txt index ca8e272..89cff21 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,12 @@ set(common_gcc_flags -Wall -Wextra -pedantic -Wconversion -ffast-math) add_library(${PROJECT_NAME} INTERFACE) target_compile_options(${PROJECT_NAME} INTERFACE ${common_gcc_flags}) +target_compile_definitions(${PROJECT_NAME} + INTERFACE $<$:KAK_DEBUG> +) +target_include_directories(${PROJECT_NAME} + INTERFACE lib/kakoune +) add_subdirectory(lib/clooneljump/src/cloonelgraphics) add_subdirectory(src/gamelib) diff --git a/lib/kakoune/ref_ptr.hh b/lib/kakoune/ref_ptr.hh new file mode 100644 index 0000000..5711ced --- /dev/null +++ b/lib/kakoune/ref_ptr.hh @@ -0,0 +1,115 @@ +#ifndef ref_ptr_hh_INCLUDED +#define ref_ptr_hh_INCLUDED + +#include + +namespace Kakoune +{ + +struct RefCountable +{ + int refcount = 0; + virtual ~RefCountable() = default; +}; + +struct RefCountablePolicy +{ + static void inc_ref(RefCountable* r, void*) noexcept { ++r->refcount; } + static void dec_ref(RefCountable* r, void*) { if (--r->refcount == 0) delete r; } + static void ptr_moved(RefCountable*, void*, void*) noexcept {} +}; + +template +struct RefPtr +{ + RefPtr() = default; + explicit RefPtr(T* ptr) : m_ptr(ptr) { acquire(); } + ~RefPtr() { release(); } + RefPtr(const RefPtr& other) : m_ptr(other.m_ptr) { acquire(); } + RefPtr(RefPtr&& other) + noexcept(noexcept(std::declval().moved(nullptr))) + : m_ptr(other.m_ptr) { other.m_ptr = nullptr; moved(&other); } + + RefPtr& operator=(const RefPtr& other) + { + if (other.m_ptr != m_ptr) + { + release(); + m_ptr = other.m_ptr; + acquire(); + } + return *this; + } + + RefPtr& operator=(RefPtr&& other) + { + release(); + m_ptr = other.m_ptr; + other.m_ptr = nullptr; + moved(&other); + return *this; + } + + RefPtr& operator=(T* ptr) + { + if (ptr != m_ptr) + { + release(); + m_ptr = ptr; + acquire(); + } + return *this; + } + + [[gnu::always_inline]] + T* operator->() const { return m_ptr; } + [[gnu::always_inline]] + T& operator*() const { return *m_ptr; } + + [[gnu::always_inline]] + T* get() const { return m_ptr; } + + [[gnu::always_inline]] + explicit operator bool() const { return m_ptr; } + + void reset(T* ptr = nullptr) + { + if (ptr == m_ptr) + return; + release(); + m_ptr = ptr; + acquire(); + } + + friend bool operator==(const RefPtr& lhs, const RefPtr& rhs) { return lhs.m_ptr == rhs.m_ptr; } + friend bool operator!=(const RefPtr& lhs, const RefPtr& rhs) { return lhs.m_ptr != rhs.m_ptr; } + +private: + T* m_ptr = nullptr; + + [[gnu::always_inline]] + void acquire() + { + if (m_ptr) + Policy::inc_ref(m_ptr, this); + } + + [[gnu::always_inline]] + void release() + { + if (m_ptr) + Policy::dec_ref(m_ptr, this); + } + + [[gnu::always_inline]] + void moved(void* from) + noexcept(noexcept(Policy::ptr_moved(nullptr, nullptr, nullptr))) + { + if (m_ptr) + Policy::ptr_moved(m_ptr, from, this); + } +}; + +} + +#endif // ref_ptr_hh_INCLUDED diff --git a/lib/kakoune/safe_ptr.hh b/lib/kakoune/safe_ptr.hh new file mode 100644 index 0000000..4d83a46 --- /dev/null +++ b/lib/kakoune/safe_ptr.hh @@ -0,0 +1,96 @@ +#ifndef safe_ptr_hh_INCLUDED +#define safe_ptr_hh_INCLUDED + +// #define SAFE_PTR_TRACK_CALLSTACKS + +#include "assert.hh" +#include "ref_ptr.hh" + +#include +#include + +#ifdef SAFE_PTR_TRACK_CALLSTACKS +#include "backtrace.hh" +#include "vector.hh" +#include +#endif + +namespace Kakoune +{ + +// *** SafePtr: objects that assert nobody references them when they die *** + +class SafeCountable +{ +public: +#ifdef KAK_DEBUG + SafeCountable() : m_count(0) {} + ~SafeCountable() + { + kak_assert(m_count == 0); + #ifdef SAFE_PTR_TRACK_CALLSTACKS + kak_assert(m_callstacks.empty()); + #endif + } + +private: + friend struct SafeCountablePolicy; + #ifdef SAFE_PTR_TRACK_CALLSTACKS + struct Callstack + { + Callstack(void* p) : ptr(p) {} + void* ptr; + Backtrace bt; + }; + + mutable Vector m_callstacks; + #endif + mutable int m_count; +#endif +}; + +struct SafeCountablePolicy +{ +#ifdef KAK_DEBUG + static void inc_ref(const SafeCountable* sc, void* ptr) noexcept + { + ++sc->m_count; + #ifdef SAFE_PTR_TRACK_CALLSTACKS + sc->m_callstacks.emplace_back(ptr); + #endif + } + + static void dec_ref(const SafeCountable* sc, void* ptr) noexcept + { + --sc->m_count; + kak_assert(sc->m_count >= 0); + #ifdef SAFE_PTR_TRACK_CALLSTACKS + auto it = std::find_if(sc->m_callstacks.begin(), sc->m_callstacks.end(), + [=](const SafeCountable::Callstack& cs) { return cs.ptr == ptr; }); + kak_assert(it != sc->m_callstacks.end()); + sc->m_callstacks.erase(it); + #endif + } + + static void ptr_moved(const SafeCountable* sc, void* from, void* to) noexcept + { + #ifdef SAFE_PTR_TRACK_CALLSTACKS + auto it = std::find_if(sc->m_callstacks.begin(), sc->m_callstacks.end(), + [=](const SafeCountable::Callstack& cs) { return cs.ptr == from; }); + kak_assert(it != sc->m_callstacks.end()); + it->ptr = to; + #endif + } +#else + static void inc_ref(const SafeCountable*, void* ptr) noexcept {} + static void dec_ref(const SafeCountable*, void* ptr) noexcept {} + static void ptr_moved(const SafeCountable*, void*, void*) noexcept {} +#endif +}; + +template +using SafePtr = RefPtr; + +} + +#endif // safe_ptr_hh_INCLUDED