diff --git a/libs/util/include/psemek/util/movable_function.hpp b/libs/util/include/psemek/util/movable_function.hpp index efe8b20a..6bdc4a5c 100644 --- a/libs/util/include/psemek/util/movable_function.hpp +++ b/libs/util/include/psemek/util/movable_function.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include namespace psemek::util @@ -51,34 +53,37 @@ namespace psemek::util { using signature = R(Args...); - movable_function() = default; + movable_function() noexcept = default; template movable_function(F f) - : p {std::make_unique>>(std::move(f))} - {} + { + storage_.template emplace>>(std::move(f)); + } - movable_function (movable_function &&) = default; - movable_function & operator = (movable_function &&) = default; + movable_function (movable_function &&) noexcept = default; + movable_function & operator = (movable_function &&) noexcept = default; movable_function (movable_function const &) = delete; movable_function & operator = (movable_function const &) = delete; explicit operator bool() const { - return static_cast(p); + return static_cast(storage_); } - R operator()(Args ... args) const + template + R operator()(Args1 && ... args) const { - if (!p) + if (!storage_) detail::bad_function_call(); - return p->call(args...); + return storage_.get()->call(std::forward(args)...); } private: - std::unique_ptr> p; + static constexpr std::size_t small_storage_size = sizeof(void*) * 3; + polymorhpic_storage, small_storage_size> storage_; }; } diff --git a/libs/util/include/psemek/util/polymorphic_storage.hpp b/libs/util/include/psemek/util/polymorphic_storage.hpp new file mode 100644 index 00000000..b35e7652 --- /dev/null +++ b/libs/util/include/psemek/util/polymorphic_storage.hpp @@ -0,0 +1,155 @@ +#pragma once + +#include +#include + +namespace psemek::util +{ + + template + struct polymorhpic_storage + { + static constexpr std::size_t align = std::max(alignof(I*), alignof(I)); + static constexpr std::size_t size = std::max(sizeof(I*), std::max(sizeof(I), Size)); + + polymorhpic_storage() noexcept = default; + + polymorhpic_storage(polymorhpic_storage && other) noexcept; + polymorhpic_storage & operator = (polymorhpic_storage && other) noexcept; + + polymorhpic_storage(polymorhpic_storage const &) = delete; + polymorhpic_storage & operator = (polymorhpic_storage const &) = delete; + + template + void emplace(Args && ... args); + + I * get() const noexcept; + + void reset() noexcept; + + ~polymorhpic_storage() noexcept + { + reset(); + } + + explicit operator bool() const + { + return static_cast(manager_); + } + + private: + std::aligned_storage_t storage_; + + using manager = void*(*)(void *, void *, int); + manager manager_ = nullptr; + + static constexpr int op_get = 1; + static constexpr int op_move = 2; + static constexpr int op_destroy = 3; + }; + + template + polymorhpic_storage::polymorhpic_storage(polymorhpic_storage && other) noexcept + { + manager_ = other.manager_; + if (manager_) + { + manager_(&other.storage_, &storage_, op_move); + } + + other.reset(); + } + + template + polymorhpic_storage & polymorhpic_storage::operator = (polymorhpic_storage && other) noexcept + { + if (this == &other) + return *this; + + reset(); + manager_ = other.manager_; + if (manager_) + { + manager_(&other.storage_, &storage_, op_move); + } + + other.reset(); + return *this; + } + + template + template + void polymorhpic_storage::emplace(Args && ... args) + { + constexpr bool static_storage = true + && (sizeof(T) <= size) + && (alignof(T) <= align) + && ((align % alignof(T)) == 0) + && std::is_nothrow_move_constructible_v + ; + + reset(); + if constexpr (static_storage) + { + static_assert(sizeof(T) <= size); + new (reinterpret_cast(&storage_)) T(std::forward(args)...); + manager_ = [](void * src, void * dst, int op) -> void* + { + switch (op) + { + case op_get: + return src; + case op_move: + new (reinterpret_cast(dst)) T(std::move(*reinterpret_cast(src))); + break; + case op_destroy: + reinterpret_cast(src)->~T(); + break; + default: + break; + } + + return nullptr; + }; + } + else + { + *reinterpret_cast(&storage_) = new T(std::forward(args)...); + manager_ = [](void * src, void * dst, int op) -> void* + { + switch (op) + { + case op_get: + return *reinterpret_cast(src); + case op_move: + *reinterpret_cast(dst) = *reinterpret_cast(src); + *reinterpret_cast(src) = nullptr; + break; + case op_destroy: + delete *reinterpret_cast(src); + break; + default: + break; + } + + return nullptr; + }; + } + } + + template + I * polymorhpic_storage::get() const noexcept + { + if (!manager_) return nullptr; + return static_cast(manager_(const_cast(static_cast(&storage_)), nullptr, op_get)); + } + + template + void polymorhpic_storage::reset() noexcept + { + if (!manager_) return; + manager_(&storage_, nullptr, op_destroy); + manager_ = nullptr; + } + +}