xmultimethods.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. /***************************************************************************
  2. * Copyright (c) Johan Mabille, Sylvain Corlay and Wolf Vollprecht *
  3. * Copyright (c) QuantStack *
  4. * *
  5. * Distributed under the terms of the BSD 3-Clause License. *
  6. * *
  7. * The full license is in the file LICENSE, distributed with this software. *
  8. ****************************************************************************/
  9. #ifndef XTL_MULTIMETHODS_HPP
  10. #define XTL_MULTIMETHODS_HPP
  11. #include <array>
  12. #include <cstdint>
  13. #include <functional>
  14. #include <map>
  15. #include <typeindex>
  16. #include <type_traits>
  17. #include <vector>
  18. #include "xmeta_utils.hpp"
  19. namespace xtl
  20. {
  21. // Loki's multimethods ported to modern C++ and generalized to N arguments
  22. // Original implementation can be found at
  23. // https://github.com/snaewe/loki-lib/blob/master/include/loki/MultiMethods.h
  24. struct symmetric_dispatch {};
  25. struct antisymmetric_dispatch {};
  26. /*********************
  27. * static_dispatcher *
  28. *********************/
  29. template
  30. <
  31. class executor,
  32. class base_lhs,
  33. class lhs_type_list,
  34. class return_type = void,
  35. class symmetric = antisymmetric_dispatch,
  36. class base_rhs = base_lhs,
  37. class rhs_type_list = lhs_type_list
  38. >
  39. class static_dispatcher
  40. {
  41. private:
  42. template <class lhs_type, class rhs_type>
  43. static return_type invoke_executor(lhs_type& lhs,
  44. rhs_type& rhs,
  45. executor& exec,
  46. std::false_type)
  47. {
  48. return exec.run(lhs, rhs);
  49. }
  50. template <class lhs_type, class rhs_type>
  51. static return_type invoke_executor(lhs_type& lhs,
  52. rhs_type& rhs,
  53. executor& exec,
  54. std::true_type)
  55. {
  56. return exec.run(rhs, lhs);
  57. }
  58. template <class lhs_type>
  59. static return_type dispatch_rhs(lhs_type& lhs,
  60. base_rhs& rhs,
  61. executor& exec,
  62. mpl::vector<>)
  63. {
  64. return exec.on_error(lhs, rhs);
  65. }
  66. template <class lhs_type, class T, class... U>
  67. static return_type dispatch_rhs(lhs_type& lhs,
  68. base_rhs& rhs,
  69. executor& exec,
  70. mpl::vector<T, U...>)
  71. {
  72. if (T* p = dynamic_cast<T*>(&rhs))
  73. {
  74. constexpr std::size_t lhs_index = mpl::index_of<lhs_type_list, lhs_type>::value;
  75. constexpr std::size_t rhs_index = mpl::index_of<rhs_type_list, T>::value;
  76. using invoke_flag = std::integral_constant<bool,
  77. std::is_same<symmetric, symmetric_dispatch>::value && (rhs_index < lhs_index)>;
  78. return invoke_executor(lhs, *p, exec, invoke_flag());
  79. }
  80. return dispatch_rhs(lhs, rhs, exec, mpl::vector<U...>());
  81. }
  82. static return_type dispatch_lhs(base_lhs& lhs,
  83. base_rhs& rhs,
  84. executor& exec,
  85. mpl::vector<>)
  86. {
  87. return exec.on_error(lhs, rhs);
  88. }
  89. template <class T, class... U>
  90. static return_type dispatch_lhs(base_lhs& lhs,
  91. base_rhs& rhs,
  92. executor& exec,
  93. mpl::vector<T, U...>)
  94. {
  95. if (T* p = dynamic_cast<T*>(&lhs))
  96. {
  97. return dispatch_rhs(*p, rhs, exec, rhs_type_list());
  98. }
  99. return dispatch_lhs(lhs, rhs, exec, mpl::vector<U...>());
  100. }
  101. public:
  102. static return_type dispatch(base_lhs& lhs, base_rhs& rhs, executor& exec)
  103. {
  104. return dispatch_lhs(lhs, rhs, exec, lhs_type_list());
  105. }
  106. };
  107. // TODO: generalize to N-D with mpl::vector of mpl:vector
  108. // Warning: this is hardcore ;)
  109. /********************
  110. * basic_dispatcher *
  111. ********************/
  112. template
  113. <
  114. class type_list,
  115. class return_type,
  116. class undispatched_type_list,
  117. class callback_type
  118. >
  119. class basic_dispatcher;
  120. template
  121. <
  122. class return_type,
  123. class callback_type,
  124. class... B,
  125. class... T
  126. >
  127. class basic_dispatcher<mpl::vector<B...>, return_type, mpl::vector<T...>, callback_type>
  128. {
  129. private:
  130. using key_type = std::array<std::type_index, sizeof...(B)>;
  131. using map_type = std::map<key_type, callback_type>;
  132. map_type m_callback_map;
  133. template <class... U>
  134. key_type make_key() const
  135. {
  136. return {{std::type_index(typeid(U))...}};
  137. }
  138. public:
  139. template <class... D>
  140. void insert(callback_type&& cb)
  141. {
  142. static_assert(sizeof...(D) == sizeof...(B),
  143. "Number of callback arguments must match dispatcher dimension");
  144. m_callback_map[make_key<D...>()] = std::move(cb);
  145. }
  146. template <class... D>
  147. void erase()
  148. {
  149. static_assert(sizeof...(D) == sizeof...(B),
  150. "Number of callback arguments must match dispatcher dimension");
  151. m_callback_map.erase(make_key<D...>());
  152. }
  153. inline return_type dispatch(B&... args, T&... udargs) const
  154. {
  155. key_type k = {{std::type_index(typeid(args))...}};
  156. auto it = m_callback_map.find(k);
  157. if (it == m_callback_map.end())
  158. {
  159. XTL_THROW(std::runtime_error, "callback not found");
  160. }
  161. return (it->second)(args..., udargs...);
  162. }
  163. };
  164. /*************************
  165. * basic_fast_dispatcher *
  166. *************************/
  167. #define XTL_IMPLEMENT_INDEXABLE_CLASS() \
  168. static std::size_t& get_class_static_index()\
  169. { \
  170. static std::size_t index = SIZE_MAX; \
  171. return index; \
  172. } \
  173. virtual std::size_t get_class_index() const \
  174. { \
  175. return get_class_static_index(); \
  176. }
  177. namespace detail
  178. {
  179. template <class T>
  180. class recursive_container_impl : private std::vector<T>
  181. {
  182. public:
  183. using base_type = std::vector<T>;
  184. using base_type::base_type;
  185. using base_type::operator[];
  186. using base_type::size;
  187. using base_type::resize;
  188. };
  189. template <class callback_type, std::size_t level>
  190. class recursive_container
  191. : public recursive_container_impl<recursive_container<callback_type, level-1>>
  192. {
  193. };
  194. template <class callback>
  195. class recursive_container<callback, 0>
  196. : public recursive_container_impl<callback>
  197. {
  198. };
  199. }
  200. template
  201. <
  202. class type_list,
  203. class return_type,
  204. class undispatched_type_list,
  205. class callback_type
  206. >
  207. class basic_fast_dispatcher;
  208. template
  209. <
  210. class return_type,
  211. class callback_type,
  212. class... B,
  213. class... T
  214. >
  215. class basic_fast_dispatcher<mpl::vector<B...>, return_type, mpl::vector<T...>, callback_type>
  216. {
  217. private:
  218. static constexpr std::size_t nb_args = sizeof...(B);
  219. using storage_type = detail::recursive_container<callback_type, sizeof...(B) - 1>;
  220. using index_type = std::array<std::size_t, nb_args>;
  221. using index_ref_type = std::array<std::reference_wrapper<std::size_t>, nb_args>;
  222. storage_type m_callbacks;
  223. std::size_t m_next_index;
  224. template <std::size_t I, class C>
  225. void resize_container(C& c, const index_ref_type& index)
  226. {
  227. std::size_t& idx = index[I];
  228. if (idx == SIZE_MAX)
  229. {
  230. c.resize(++m_next_index);
  231. idx = c.size() - 1u;
  232. }
  233. else if(c.size() <= idx)
  234. {
  235. c.resize(idx + 1u);
  236. }
  237. }
  238. template <std::size_t I, class C>
  239. std::enable_if_t<I + 1 == nb_args>
  240. insert_impl(callback_type&& cb, C& c, const index_ref_type& index)
  241. {
  242. resize_container<I>(c, index);
  243. c[index[I]] = std::move(cb);
  244. }
  245. template <std::size_t I, class C>
  246. std::enable_if_t<I + 1 != nb_args>
  247. insert_impl(callback_type&& cb, C& c, const index_ref_type& index)
  248. {
  249. resize_container<I>(c, index);
  250. insert_impl<I+1>(std::move(cb), c[index[I]], index);
  251. }
  252. template <std::size_t I, class C>
  253. void check_size(C& c, const index_type& index) const
  254. {
  255. if (index[I] >= c.size())
  256. {
  257. XTL_THROW(std::runtime_error, "callback not found");
  258. }
  259. }
  260. template <std::size_t I, class C>
  261. std::enable_if_t<I + 1 == nb_args, return_type>
  262. dispatch_impl(C& c, const index_type& index, B&... args, T&... udargs) const
  263. {
  264. check_size<I>(c, index);
  265. return c[index[I]](args..., udargs...);
  266. }
  267. template <std::size_t I, class C>
  268. std::enable_if_t<I + 1 != nb_args, return_type>
  269. dispatch_impl(C& c, const index_type& index, B&... args, T&... udargs) const
  270. {
  271. check_size<I>(c, index);
  272. return dispatch_impl<I+1>(c[index[I]], index, args..., udargs...);
  273. }
  274. public:
  275. inline basic_fast_dispatcher()
  276. : m_next_index(0)
  277. {
  278. }
  279. template <class... D>
  280. void insert(callback_type&& cb)
  281. {
  282. static_assert(sizeof...(D) == sizeof...(B),
  283. "Number of callback arguments must match dispatcher dimension");
  284. index_ref_type index = {{std::ref(D::get_class_static_index())...}};
  285. insert_impl<0>(std::move(cb), m_callbacks, index);
  286. }
  287. inline return_type dispatch(B&... args, T&... udargs) const
  288. {
  289. index_type index = {{args.get_class_index()...}};
  290. return dispatch_impl<0>(m_callbacks, index, args..., udargs...);
  291. }
  292. };
  293. /******************************
  294. * dynamic and static casters *
  295. ******************************/
  296. template <class T, class F>
  297. struct static_caster
  298. {
  299. static T& cast(F& f)
  300. {
  301. return static_cast<T&>(f);
  302. }
  303. };
  304. template <class T, class F>
  305. struct dynamic_caster
  306. {
  307. static T& cast(F& f)
  308. {
  309. return dynamic_cast<T&>(f);
  310. }
  311. };
  312. /**********************
  313. * functor_dispatcher *
  314. **********************/
  315. template
  316. <
  317. class type_list,
  318. class return_type,
  319. class undispatched_type = mpl::vector<>,
  320. template <class, class> class casting_policy = dynamic_caster,
  321. template <class, class, class, class> class dispatcher = basic_dispatcher
  322. >
  323. class functor_dispatcher;
  324. template
  325. <
  326. class return_type,
  327. template <class, class> class casting_policy,
  328. template <class, class, class, class> class dispatcher,
  329. class... B,
  330. class... T
  331. >
  332. class functor_dispatcher<mpl::vector<B...>, return_type, mpl::vector<T...>, casting_policy, dispatcher>
  333. {
  334. private:
  335. using functor_type = std::function<return_type (B&..., T&...)>;
  336. using backend = dispatcher<mpl::vector<B...>,
  337. return_type,
  338. mpl::vector<T...>,
  339. functor_type>;
  340. backend m_backend;
  341. public:
  342. template <class... D, class Fun>
  343. void insert(const Fun& fun)
  344. {
  345. functor_type f([fun](B&... args, T&... udargs) -> return_type
  346. {
  347. return fun(casting_policy<D&, B&>::cast(args)..., udargs...);
  348. });
  349. m_backend.template insert<D...>(std::move(f));
  350. }
  351. template <class... D>
  352. void erase()
  353. {
  354. m_backend.template erase<D...>();
  355. }
  356. inline return_type dispatch(B&... args, T&... udargs) const
  357. {
  358. return m_backend.dispatch(args..., udargs...);
  359. }
  360. };
  361. }
  362. #endif