You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

146 regels
4.2 KiB

  1. #pragma once
  2. #include <ecs/config.h>
  3. #include "./storage_cast.h"
  4. beg_namespace_ecs_core_utils
  5. {
  6. template<typename T_signature, size_t T_storage_size = 64>
  7. struct fixed_function;
  8. template<typename T_return, typename... T_args, size_t T_storage_size>
  9. struct fixed_function<T_return(T_args...), T_storage_size>
  10. {
  11. private:
  12. static constexpr decltype(auto) storage_size = T_storage_size;
  13. using return_type = T_return;
  14. using storage_type = std::aligned_storage_t<storage_size, alignof(size_t)>;
  15. using function_ptr_type = return_type (*)(T_args...);
  16. using method_type = return_type (*)(storage_type*, function_ptr_type, T_args...);
  17. using allocator_type = void (*)(storage_type*, void*);
  18. private:
  19. union
  20. {
  21. storage_type _storage;
  22. function_ptr_type _function_ptr;
  23. };
  24. method_type _method_ptr;
  25. allocator_type _allocator_ptr;
  26. private:
  27. void move_from_other(fixed_function& other) noexcept
  28. {
  29. assert(this != &o);
  30. // cleanup
  31. if (_allocator_ptr)
  32. {
  33. _allocator_ptr(&_storage, nullptr);
  34. }
  35. _allocator_ptr = nullptr;
  36. _function_ptr = nullptr;
  37. // move
  38. _method_ptr = other._method_ptr;
  39. other._method_ptr = nullptr;
  40. _function_ptr = other._function_ptr;
  41. _allocator_ptr = other._allocator_ptr;
  42. if (_allocator_ptr)
  43. {
  44. _allocator_ptr(&_storage, &other._storage);
  45. }
  46. }
  47. public:
  48. inline fixed_function() noexcept
  49. : _function_ptr (nullptr)
  50. , _method_ptr (nullptr)
  51. , _allocator_ptr(nullptr)
  52. { }
  53. template<typename T_func>
  54. inline fixed_function(T_func&& func) noexcept
  55. : fixed_function()
  56. {
  57. using unref_type = std::remove_reference_t<T_func>;
  58. static_assert(
  59. sizeof(unref_type) < storage_size,
  60. "functional object doesn't fit into internal storage");
  61. static_assert(
  62. std::is_move_constructible<unref_type> { },
  63. "should be move constructable");
  64. _method_ptr = [](storage_type* s, function_ptr_type, T_args... args)
  65. {
  66. return storage_cast<unref_type>(s)->operator()(args...);
  67. };
  68. _allocator_ptr = [](storage_type* s, void* obj)
  69. {
  70. if (obj)
  71. {
  72. new(s) unref_type(std::move(*static_cast<unref_type*>(obj)));
  73. }
  74. else
  75. {
  76. storage_cast<unref_type>(s)->~unref_type();
  77. }
  78. };
  79. _allocator_ptr(&_storage, &func);
  80. }
  81. template<typename TF_return, typename... TF_args>
  82. inline fixed_function(TF_return (*func)(TF_args...)) noexcept
  83. : fixed_function()
  84. {
  85. _function_ptr = func;
  86. _method_ptr = [](storage_type*, function_ptr_type f, T_args... args)
  87. {
  88. return static_cast<decltype(func)>(f)(args...);
  89. };
  90. }
  91. inline fixed_function(fixed_function&& other) noexcept
  92. : fixed_function()
  93. { move_from_other(other); }
  94. fixed_function(const fixed_function&) = delete;
  95. inline ~fixed_function() noexcept
  96. {
  97. if (_allocator_ptr)
  98. {
  99. _allocator_ptr(&_storage, nullptr);
  100. }
  101. _allocator_ptr = nullptr;
  102. _function_ptr = nullptr;
  103. _method_ptr = nullptr;
  104. }
  105. inline fixed_function& operator=(fixed_function&& other) noexcept
  106. {
  107. move_from_other(other);
  108. return *this;
  109. }
  110. fixed_function& operator=(const fixed_function&) = delete;
  111. template<typename... TF_args>
  112. inline decltype(auto) operator()(TF_args&&... args) const
  113. {
  114. assert(_method_ptr != nullptr);
  115. return _method_ptr(&_storage, _function_ptr, std::forward<TF_args>(args)...);
  116. }
  117. };
  118. }
  119. end_namespace_ecs_core_utils