function.hpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. //
  2. // https://github.com/lisyarus/movable_function
  3. //
  4. // bench (Intel(R) Core(TM) i5-4590 CPU @ 3.30GHz, RAM 8.00 GB)
  5. //
  6. // Assign light lambda (std::function) took 2.03859s
  7. // Assign light lambda (function) took 0.201078s
  8. // Call light lambda (std::function) took 0.206337s
  9. // Call light lambda (function) took 0.18224s
  10. // counter = 200000000
  11. // Assign heavy lambda (std::function) took 15.1772s
  12. // Assign heavy lambda (function) took 13.9959s
  13. // Call heavy lambda (std::function) took 0.200089s
  14. // Call heavy lambda (function) took 0.17089s
  15. // counter = 200000000
  16. // Assign function pointer (std::function) took 1.83542s
  17. // Assign function pointer (function) took 0.200447s
  18. // Call function pointer (std::function) took 0.199261s
  19. // Call function pointer (function) took 0.226355s
  20. // global_counter = 200000000
  21. // Assign pointer to member (std::function) took 1.88201s
  22. // Assign pointer to member (function) took 0.225137s
  23. // Call pointer to member (std::function) took 0.196708s
  24. // Call pointer to member (function) took 0.225984s
  25. // x.counter = 200000000
  26. //
  27. #ifndef __ASIO2_FUNCTION_HPP__
  28. #define __ASIO2_FUNCTION_HPP__
  29. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  30. #pragma once
  31. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  32. #include <type_traits>
  33. #include <memory>
  34. #include <functional>
  35. #include <asio2/config.hpp>
  36. #include <asio2/base/log.hpp>
  37. namespace asio2::detail
  38. {
  39. #if defined(ASIO2_ENABLE_LOG) && defined(ASIO2_ENABLE_LOG_STORAGE_SIZE)
  40. template<typename = void>
  41. inline void log_function_storage_size(bool is_stack, std::size_t size)
  42. {
  43. static std::mutex mtx;
  44. static std::map<std::size_t, std::size_t> stack_map;
  45. static std::map<std::size_t, std::size_t> heaps_map;
  46. std::lock_guard guard(mtx);
  47. if (is_stack)
  48. stack_map[size]++;
  49. else
  50. heaps_map[size]++;
  51. static auto t1 = std::chrono::steady_clock::now();
  52. auto t2 = std::chrono::steady_clock::now();
  53. if (std::chrono::duration_cast<std::chrono::seconds>(t2 - t1).count() > 60)
  54. {
  55. t1 = std::chrono::steady_clock::now();
  56. std::string str;
  57. str += "\n";
  58. str += "function storage size test: ";
  59. if /**/ constexpr (sizeof(void*) == sizeof(std::uint64_t))
  60. str += "x64";
  61. else if constexpr (sizeof(void*) == sizeof(std::uint32_t))
  62. str += "x86";
  63. else
  64. str += std::to_string(sizeof(void*)) + "bit";
  65. str += ", ";
  66. #if defined(_DEBUG) || defined(DEBUG)
  67. str += "Debug";
  68. #else
  69. str += "Release";
  70. #endif
  71. #if defined(ASIO2_ENABLE_SSL) || defined(ASIO2_USE_SSL)
  72. str += ", SSL";
  73. #endif
  74. #if ASIO2_OS_LINUX || ASIO2_OS_UNIX
  75. str += ", linux";
  76. #elif ASIO2_OS_WINDOWS
  77. str += ", windows";
  78. #elif ASIO2_OS_MACOS
  79. str += ", macos";
  80. #endif
  81. str += "\n";
  82. str += "------------------------------------------------------------\n";
  83. str += "stack_map\n";
  84. for (auto [len, num] : stack_map)
  85. {
  86. str += " ";
  87. str += std::to_string(len); str += " : ";
  88. str += std::to_string(num); str += "\n";
  89. }
  90. str += "heaps_map\n";
  91. for (auto [len, num] : heaps_map)
  92. {
  93. str += " ";
  94. str += std::to_string(len); str += " : ";
  95. str += std::to_string(num); str += "\n";
  96. }
  97. str += "------------------------------------------------------------\n";
  98. str += "\n";
  99. ASIO2_LOG_FATAL("{}", str);
  100. }
  101. }
  102. #endif
  103. }
  104. namespace asio2::detail
  105. {
  106. template<class args_t>
  107. struct function_size_traits
  108. {
  109. template<class, class = void>
  110. struct has_member_size : std::false_type {};
  111. template<class T>
  112. struct has_member_size<T, std::void_t<decltype(T::function_storage_size)>> : std::true_type {};
  113. static constexpr std::size_t calc()
  114. {
  115. if constexpr (has_member_size<args_t>::value)
  116. {
  117. return args_t::function_storage_size;
  118. }
  119. else
  120. {
  121. return 0;
  122. }
  123. }
  124. static constexpr std::size_t value = calc();
  125. };
  126. template <typename Signature, std::size_t StorageSize = 0>
  127. struct function;
  128. template <typename R, typename ... Args, std::size_t StorageSize>
  129. struct function<R(Args...), StorageSize>
  130. {
  131. private:
  132. static constexpr std::size_t get_storage_size() noexcept
  133. {
  134. #ifdef ASIO2_FUNCTION_STORAGE_SIZE
  135. // if the user defined a custom function storage size, use it.
  136. return std::size_t(ASIO2_FUNCTION_STORAGE_SIZE);
  137. #else
  138. if constexpr (StorageSize >= sizeof(void*) && StorageSize <= std::size_t(1024))
  139. {
  140. return StorageSize;
  141. }
  142. else
  143. {
  144. if constexpr (sizeof(void*) == sizeof(std::uint32_t))
  145. return (sizeof(void*) * 10);
  146. else
  147. return (sizeof(void*) * 7);
  148. }
  149. #endif
  150. }
  151. static constexpr std::size_t storage_size = get_storage_size();
  152. static constexpr std::size_t storage_align = alignof(void*);
  153. template <typename T>
  154. struct uses_static_storage
  155. : std::bool_constant<true
  156. && sizeof (T) <= storage_size
  157. && alignof(T) <= storage_align
  158. && ((storage_align % alignof(T)) == 0)
  159. && std::is_nothrow_move_constructible_v<T>
  160. >
  161. {};
  162. public:
  163. using signature = R(Args...);
  164. function() noexcept = default;
  165. template <typename F>
  166. function(F && f)
  167. {
  168. assign(std::forward<F>(f));
  169. }
  170. function (function && other) noexcept
  171. {
  172. vtable_ = other.vtable_;
  173. if (vtable_)
  174. vtable_->move(std::addressof(other.storage_), std::addressof(storage_));
  175. other.reset();
  176. }
  177. function & operator = (function && other) noexcept
  178. {
  179. if (this == &other)
  180. return *this;
  181. reset();
  182. vtable_ = other.vtable_;
  183. if (vtable_)
  184. vtable_->move(std::addressof(other.storage_), std::addressof(storage_));
  185. other.reset();
  186. return *this;
  187. }
  188. function (function const &) = delete;
  189. function & operator = (function const &) = delete;
  190. ~function()
  191. {
  192. reset();
  193. }
  194. // operator = (F && f) has strong exception guarantree:
  195. // if the assignment throws, the function remains unchanged
  196. template <typename F>
  197. std::enable_if_t<uses_static_storage<std::decay_t<F>>::value, function &>
  198. operator = (F && f)
  199. {
  200. reset();
  201. assign(std::forward<F>(f));
  202. return *this;
  203. }
  204. template <typename F>
  205. std::enable_if_t<!uses_static_storage<std::decay_t<F>>::value, function &>
  206. operator = (F && f)
  207. {
  208. function(std::forward<F>(f)).swap(*this);
  209. return *this;
  210. }
  211. explicit operator bool() const
  212. {
  213. return static_cast<bool>(vtable_);
  214. }
  215. template <typename ... Args1>
  216. R operator()(Args1 && ... args) const
  217. {
  218. if (!vtable_)
  219. throw std::bad_function_call();
  220. return vtable_->call(const_cast<void *>(static_cast<void const *>(&storage_)), std::forward<Args1>(args)...);
  221. }
  222. void reset()
  223. {
  224. if (!vtable_) return;
  225. vtable_->destroy(&storage_);
  226. vtable_ = nullptr;
  227. }
  228. void swap(function & other) noexcept
  229. {
  230. std::swap(*this, other);
  231. }
  232. private:
  233. std::aligned_storage_t<storage_size, storage_align> storage_;
  234. struct vtable
  235. {
  236. using move_func = void(*)(void *, void *);
  237. using destroy_func = void(*)(void *);
  238. using call_func = R(*)(void *, Args&& ...);
  239. move_func move;
  240. destroy_func destroy;
  241. call_func call;
  242. };
  243. vtable * vtable_ = nullptr;
  244. template <typename F>
  245. void assign(F && f)
  246. {
  247. using T = std::decay_t<F>;
  248. if constexpr (uses_static_storage<T>::value)
  249. {
  250. new (reinterpret_cast<T *>(&storage_)) T(std::move(f));
  251. #if defined(ASIO2_ENABLE_LOG) && defined(ASIO2_ENABLE_LOG_STORAGE_SIZE)
  252. log_function_storage_size(true, sizeof(T));
  253. #endif
  254. static vtable m = {
  255. [](void * src, void * dst){ new (reinterpret_cast<T*>(dst)) T(std::move(*reinterpret_cast<T*>(src))); },
  256. [](void * src){ reinterpret_cast<T*>(src)->~T(); },
  257. [](void * src, Args&& ... args) -> R { return std::invoke(*reinterpret_cast<T*>(src), static_cast<Args&&>(args)...); }
  258. };
  259. vtable_ = &m;
  260. }
  261. else
  262. {
  263. *reinterpret_cast<T**>(&storage_) = new T(std::move(f));
  264. #if defined(ASIO2_ENABLE_LOG) && defined(ASIO2_ENABLE_LOG_STORAGE_SIZE)
  265. log_function_storage_size(false, sizeof(T));
  266. #endif
  267. static vtable m = {
  268. [](void * src, void * dst){ *reinterpret_cast<T**>(dst) = *reinterpret_cast<T**>(src); *reinterpret_cast<T**>(src) = nullptr; },
  269. [](void * src){ delete *reinterpret_cast<T**>(src); },
  270. [](void * src, Args&& ... args) -> R { return std::invoke(**reinterpret_cast<T**>(src), static_cast<Args&&>(args)...); }
  271. };
  272. vtable_ = &m;
  273. }
  274. }
  275. };
  276. }
  277. #endif // !__ASIO2_FUNCTION_HPP__