diff --git a/include/cubescript/cubescript.hh b/include/cubescript/cubescript.hh index 2afe633..9747c3b 100644 --- a/include/cubescript/cubescript.hh +++ b/include/cubescript/cubescript.hh @@ -15,6 +15,8 @@ #include "cubescript_conf.hh" +#include "cubescript/callable.hh" + #if defined(__CYGWIN__) || (defined(_WIN32) && !defined(_XBOX_VER)) # ifdef LIBCUBESCRIPT_DLL # ifdef LIBCUBESCRIPT_BUILD @@ -46,202 +48,6 @@ struct internal_error: std::runtime_error { using std::runtime_error::runtime_error; }; -template -struct callable { -private: - struct base { - base(base const &); - base &operator=(base const &); - - public: - base() {} - virtual ~base() {} - virtual void move_to(base *) = 0; - virtual R operator()(A &&...args) = 0; - }; - - template - struct store: base { - explicit store(F &&f): p_stor{std::move(f)} {} - - virtual void move_to(base *p) { - ::new (p) store{std::move(p_stor)}; - } - - virtual R operator()(A &&...args) { - return std::invoke(*std::launder( - reinterpret_cast(&p_stor) - ), std::forward(args)...); - } - - private: - F p_stor; - }; - - using alloc_f = void *(*)(void *, void *, std::size_t, std::size_t); - - struct f_alloc { - alloc_f af; - void *ud; - size_t asize; - }; - - std::aligned_storage_t p_stor; - base *p_func; - - static inline base *as_base(void *p) { - return static_cast(p); - } - - template - static inline bool f_not_null(T const &) { return true; } - - template - static inline bool f_not_null(T *p) { return !!p; } - - template - static inline bool f_not_null(CR C::*p) { return !!p; } - - template - static inline bool f_not_null(callable const &f) { return !!f; } - - bool small_storage() { - return (static_cast(p_func) == &p_stor); - } - - void cleanup() { - if (!p_func) { - return; - } - p_func->~base(); - if (!small_storage()) { - auto &ad = *std::launder(reinterpret_cast(&p_stor)); - ad.af(ad.ud, p_func, ad.asize, 0); - } - } - -public: - callable() noexcept: p_func{nullptr} {} - callable(std::nullptr_t) noexcept: p_func{nullptr} {} - callable(std::nullptr_t, alloc_f, void *) noexcept: p_func{nullptr} {} - - callable(callable &&f) noexcept { - if (!f.p_func) { - p_func = nullptr; - } else if (f.small_storage()) { - p_func = as_base(&p_stor); - f.p_func->move_to(p_func); - } else { - p_func = f.p_func; - f.p_func = nullptr; - } - } - - template - callable(F func, alloc_f af, void *ud) { - if (!f_not_null(func)) { - return; - } - if constexpr (sizeof(store) <= sizeof(p_stor)) { - auto *p = static_cast(&p_stor); - p_func = ::new (p) store{std::move(func)}; - } else { - auto &ad = *std::launder(reinterpret_cast(&p_stor)); - ad.af = af; - ad.ud = ud; - ad.asize = sizeof(store); - p_func = static_cast *>( - af(ud, nullptr, 0, sizeof(store)) - ); - try { - new (p_func) store{std::move(func)}; - } catch (...) { - af(ud, p_func, sizeof(store), 0); - throw; - } - } - } - - callable &operator=(callable const &) = delete; - - callable &operator=(callable &&f) noexcept { - cleanup(); - if (f.p_func == nullptr) { - p_func = nullptr; - } else if (f.small_storage()) { - p_func = as_base(&p_stor); - f.p_func->move_to(p_func); - } else { - p_func = f.p_func; - f.p_func = nullptr; - } - return *this; - } - - callable &operator=(std::nullptr_t) noexcept { - cleanup(); - p_func = nullptr; - return *this; - } - - template - callable &operator=(F &&func) { - callable{std::forward(func)}.swap(*this); - return *this; - } - - ~callable() { - cleanup(); - } - - void swap(callable &f) noexcept { - std::aligned_storage_t tmp_stor; - if (small_storage() && f.small_storage()) { - auto *t = as_base(&tmp_stor); - p_func->move_to(t); - p_func->~base(); - p_func = nullptr; - f.p_func->move_to(as_base(&p_stor)); - f.p_func->~base(); - f.p_func = nullptr; - p_func = as_base(&p_stor); - t->move_to(as_base(&f.p_stor)); - t->~base(); - f.p_func = as_base(&f.p_stor); - } else if (small_storage()) { - /* copy allocator address/size */ - memcpy(&tmp_stor, &f.p_stor, sizeof(tmp_stor)); - p_func->move_to(as_base(&f.p_stor)); - p_func->~base(); - p_func = f.p_func; - f.p_func = as_base(&f.p_stor); - memcpy(&p_stor, &tmp_stor, sizeof(tmp_stor)); - } else if (f.small_storage()) { - /* copy allocator address/size */ - memcpy(&tmp_stor, &p_stor, sizeof(tmp_stor)); - f.p_func->move_to(as_base(&p_stor)); - f.p_func->~base(); - f.p_func = p_func; - p_func = as_base(&p_stor); - memcpy(&f.p_stor, &tmp_stor, sizeof(tmp_stor)); - } else { - /* copy allocator address/size */ - memcpy(&tmp_stor, &p_stor, sizeof(tmp_stor)); - memcpy(&p_stor, &f.p_stor, sizeof(tmp_stor)); - memcpy(&f.p_stor, &tmp_stor, sizeof(tmp_stor)); - std::swap(p_func, f.p_func); - } - } - - explicit operator bool() const noexcept { - return !!p_func; - } - - R operator()(A ...args) { - return (*p_func)(std::forward(args)...); - } -}; - using alloc_func = void *(*)(void *, void *, size_t, size_t); struct state; diff --git a/include/cubescript/cubescript/callable.hh b/include/cubescript/cubescript/callable.hh new file mode 100644 index 0000000..96f90db --- /dev/null +++ b/include/cubescript/cubescript/callable.hh @@ -0,0 +1,210 @@ +#ifndef LIBCUBESCRIPT_CUBESCRIPT_CALLABLE_HH +#define LIBCUBESCRIPT_CUBESCRIPT_CALLABLE_HH + +#include +#include +#include +#include +#include + +namespace cubescript { + +template +struct callable { +private: + struct base { + base(base const &); + base &operator=(base const &); + + public: + base() {} + virtual ~base() {} + virtual void move_to(base *) = 0; + virtual R operator()(A &&...args) = 0; + }; + + template + struct store: base { + explicit store(F &&f): p_stor{std::move(f)} {} + + virtual void move_to(base *p) { + ::new (p) store{std::move(p_stor)}; + } + + virtual R operator()(A &&...args) { + return std::invoke(*std::launder( + reinterpret_cast(&p_stor) + ), std::forward(args)...); + } + + private: + F p_stor; + }; + + using alloc_f = void *(*)(void *, void *, std::size_t, std::size_t); + + struct f_alloc { + alloc_f af; + void *ud; + size_t asize; + }; + + std::aligned_storage_t p_stor; + base *p_func; + + static inline base *as_base(void *p) { + return static_cast(p); + } + + template + static inline bool f_not_null(T const &) { return true; } + + template + static inline bool f_not_null(T *p) { return !!p; } + + template + static inline bool f_not_null(CR C::*p) { return !!p; } + + template + static inline bool f_not_null(callable const &f) { return !!f; } + + bool small_storage() { + return (static_cast(p_func) == &p_stor); + } + + void cleanup() { + if (!p_func) { + return; + } + p_func->~base(); + if (!small_storage()) { + auto &ad = *std::launder(reinterpret_cast(&p_stor)); + ad.af(ad.ud, p_func, ad.asize, 0); + } + } + +public: + callable() noexcept: p_func{nullptr} {} + callable(std::nullptr_t) noexcept: p_func{nullptr} {} + callable(std::nullptr_t, alloc_f, void *) noexcept: p_func{nullptr} {} + + callable(callable &&f) noexcept { + if (!f.p_func) { + p_func = nullptr; + } else if (f.small_storage()) { + p_func = as_base(&p_stor); + f.p_func->move_to(p_func); + } else { + p_func = f.p_func; + f.p_func = nullptr; + } + } + + template + callable(F func, alloc_f af, void *ud) { + if (!f_not_null(func)) { + return; + } + if constexpr (sizeof(store) <= sizeof(p_stor)) { + auto *p = static_cast(&p_stor); + p_func = ::new (p) store{std::move(func)}; + } else { + auto &ad = *std::launder(reinterpret_cast(&p_stor)); + ad.af = af; + ad.ud = ud; + ad.asize = sizeof(store); + p_func = static_cast *>( + af(ud, nullptr, 0, sizeof(store)) + ); + try { + new (p_func) store{std::move(func)}; + } catch (...) { + af(ud, p_func, sizeof(store), 0); + throw; + } + } + } + + callable &operator=(callable const &) = delete; + + callable &operator=(callable &&f) noexcept { + cleanup(); + if (f.p_func == nullptr) { + p_func = nullptr; + } else if (f.small_storage()) { + p_func = as_base(&p_stor); + f.p_func->move_to(p_func); + } else { + p_func = f.p_func; + f.p_func = nullptr; + } + return *this; + } + + callable &operator=(std::nullptr_t) noexcept { + cleanup(); + p_func = nullptr; + return *this; + } + + template + callable &operator=(F &&func) { + callable{std::forward(func)}.swap(*this); + return *this; + } + + ~callable() { + cleanup(); + } + + void swap(callable &f) noexcept { + std::aligned_storage_t tmp_stor; + if (small_storage() && f.small_storage()) { + auto *t = as_base(&tmp_stor); + p_func->move_to(t); + p_func->~base(); + p_func = nullptr; + f.p_func->move_to(as_base(&p_stor)); + f.p_func->~base(); + f.p_func = nullptr; + p_func = as_base(&p_stor); + t->move_to(as_base(&f.p_stor)); + t->~base(); + f.p_func = as_base(&f.p_stor); + } else if (small_storage()) { + /* copy allocator address/size */ + memcpy(&tmp_stor, &f.p_stor, sizeof(tmp_stor)); + p_func->move_to(as_base(&f.p_stor)); + p_func->~base(); + p_func = f.p_func; + f.p_func = as_base(&f.p_stor); + memcpy(&p_stor, &tmp_stor, sizeof(tmp_stor)); + } else if (f.small_storage()) { + /* copy allocator address/size */ + memcpy(&tmp_stor, &p_stor, sizeof(tmp_stor)); + f.p_func->move_to(as_base(&p_stor)); + f.p_func->~base(); + f.p_func = p_func; + p_func = as_base(&p_stor); + memcpy(&f.p_stor, &tmp_stor, sizeof(tmp_stor)); + } else { + /* copy allocator address/size */ + memcpy(&tmp_stor, &p_stor, sizeof(tmp_stor)); + memcpy(&p_stor, &f.p_stor, sizeof(tmp_stor)); + memcpy(&f.p_stor, &tmp_stor, sizeof(tmp_stor)); + std::swap(p_func, f.p_func); + } + } + + explicit operator bool() const noexcept { + return !!p_func; + } + + R operator()(A ...args) { + return (*p_func)(std::forward(args)...); + } +}; + +} /* namespace cubescript */ + +#endif /* LIBCUBESCRIPT_CUBESCRIPT_CALLABLE_HH */