|
- #pragma once
-
- #include <ecs/config.h>
-
- #include "./storage_cast.h"
-
- beg_namespace_ecs_core_utils
- {
-
- template<typename T_signature, size_t T_storage_size = 64>
- struct fixed_function;
-
- template<typename T_return, typename... T_args, size_t T_storage_size>
- struct fixed_function<T_return(T_args...), T_storage_size>
- {
- private:
- static constexpr decltype(auto) storage_size = T_storage_size;
-
- using return_type = T_return;
- using storage_type = std::aligned_storage_t<storage_size, alignof(size_t)>;
- using function_ptr_type = return_type (*)(T_args...);
- using method_type = return_type (*)(storage_type*, function_ptr_type, T_args...);
- using allocator_type = void (*)(storage_type*, void*);
-
- private:
- union
- {
- storage_type _storage;
- function_ptr_type _function_ptr;
- };
- method_type _method_ptr;
- allocator_type _allocator_ptr;
-
- private:
- void move_from_other(fixed_function& other) noexcept
- {
- assert(this != &o);
-
- // cleanup
- if (_allocator_ptr)
- {
- _allocator_ptr(&_storage, nullptr);
- }
- _allocator_ptr = nullptr;
- _function_ptr = nullptr;
-
- // move
- _method_ptr = other._method_ptr;
- other._method_ptr = nullptr;
-
- _function_ptr = other._function_ptr;
- _allocator_ptr = other._allocator_ptr;
- if (_allocator_ptr)
- {
- _allocator_ptr(&_storage, &other._storage);
- }
- }
-
- public:
- inline fixed_function() noexcept
- : _function_ptr (nullptr)
- , _method_ptr (nullptr)
- , _allocator_ptr(nullptr)
- { }
-
- template<typename T_func>
- inline fixed_function(T_func&& func) noexcept
- : fixed_function()
- {
- using unref_type = std::remove_reference_t<T_func>;
-
- static_assert(
- sizeof(unref_type) < storage_size,
- "functional object doesn't fit into internal storage");
-
- static_assert(
- std::is_move_constructible<unref_type> { },
- "should be move constructable");
-
- _method_ptr = [](storage_type* s, function_ptr_type, T_args... args)
- {
- return storage_cast<unref_type>(s)->operator()(args...);
- };
-
- _allocator_ptr = [](storage_type* s, void* obj)
- {
- if (obj)
- {
- new(s) unref_type(std::move(*static_cast<unref_type*>(obj)));
- }
- else
- {
- storage_cast<unref_type>(s)->~unref_type();
- }
- };
-
- _allocator_ptr(&_storage, &func);
- }
-
- template<typename TF_return, typename... TF_args>
- inline fixed_function(TF_return (*func)(TF_args...)) noexcept
- : fixed_function()
- {
- _function_ptr = func;
- _method_ptr = [](storage_type*, function_ptr_type f, T_args... args)
- {
- return static_cast<decltype(func)>(f)(args...);
- };
- }
-
- inline fixed_function(fixed_function&& other) noexcept
- : fixed_function()
- { move_from_other(other); }
-
- fixed_function(const fixed_function&) = delete;
-
- inline ~fixed_function() noexcept
- {
- if (_allocator_ptr)
- {
- _allocator_ptr(&_storage, nullptr);
- }
- _allocator_ptr = nullptr;
- _function_ptr = nullptr;
- _method_ptr = nullptr;
- }
-
- inline fixed_function& operator=(fixed_function&& other) noexcept
- {
- move_from_other(other);
- return *this;
- }
-
- fixed_function& operator=(const fixed_function&) = delete;
-
- template<typename... TF_args>
- inline decltype(auto) operator()(TF_args&&... args) const
- {
- assert(_method_ptr != nullptr);
- return _method_ptr(&_storage, _function_ptr, std::forward<TF_args>(args)...);
- }
-
- };
-
- }
- end_namespace_ecs_core_utils
|