/*************************************************************************** * Copyright (c) Johan Mabille, Sylvain Corlay and Wolf Vollprecht * * Copyright (c) QuantStack * * * * Distributed under the terms of the BSD 3-Clause License. * * * * The full license is in the file LICENSE, distributed with this software. * ****************************************************************************/ #ifndef XTL_MULTIMETHODS_HPP #define XTL_MULTIMETHODS_HPP #include #include #include #include #include #include #include #include "xmeta_utils.hpp" namespace xtl { // Loki's multimethods ported to modern C++ and generalized to N arguments // Original implementation can be found at // https://github.com/snaewe/loki-lib/blob/master/include/loki/MultiMethods.h struct symmetric_dispatch {}; struct antisymmetric_dispatch {}; /********************* * static_dispatcher * *********************/ template < class executor, class base_lhs, class lhs_type_list, class return_type = void, class symmetric = antisymmetric_dispatch, class base_rhs = base_lhs, class rhs_type_list = lhs_type_list > class static_dispatcher { private: template static return_type invoke_executor(lhs_type& lhs, rhs_type& rhs, executor& exec, std::false_type) { return exec.run(lhs, rhs); } template static return_type invoke_executor(lhs_type& lhs, rhs_type& rhs, executor& exec, std::true_type) { return exec.run(rhs, lhs); } template static return_type dispatch_rhs(lhs_type& lhs, base_rhs& rhs, executor& exec, mpl::vector<>) { return exec.on_error(lhs, rhs); } template static return_type dispatch_rhs(lhs_type& lhs, base_rhs& rhs, executor& exec, mpl::vector) { if (T* p = dynamic_cast(&rhs)) { constexpr std::size_t lhs_index = mpl::index_of::value; constexpr std::size_t rhs_index = mpl::index_of::value; using invoke_flag = std::integral_constant::value && (rhs_index < lhs_index)>; return invoke_executor(lhs, *p, exec, invoke_flag()); } return dispatch_rhs(lhs, rhs, exec, mpl::vector()); } static return_type dispatch_lhs(base_lhs& lhs, base_rhs& rhs, executor& exec, mpl::vector<>) { return exec.on_error(lhs, rhs); } template static return_type dispatch_lhs(base_lhs& lhs, base_rhs& rhs, executor& exec, mpl::vector) { if (T* p = dynamic_cast(&lhs)) { return dispatch_rhs(*p, rhs, exec, rhs_type_list()); } return dispatch_lhs(lhs, rhs, exec, mpl::vector()); } public: static return_type dispatch(base_lhs& lhs, base_rhs& rhs, executor& exec) { return dispatch_lhs(lhs, rhs, exec, lhs_type_list()); } }; // TODO: generalize to N-D with mpl::vector of mpl:vector // Warning: this is hardcore ;) /******************** * basic_dispatcher * ********************/ template < class type_list, class return_type, class undispatched_type_list, class callback_type > class basic_dispatcher; template < class return_type, class callback_type, class... B, class... T > class basic_dispatcher, return_type, mpl::vector, callback_type> { private: using key_type = std::array; using map_type = std::map; map_type m_callback_map; template key_type make_key() const { return {{std::type_index(typeid(U))...}}; } public: template void insert(callback_type&& cb) { static_assert(sizeof...(D) == sizeof...(B), "Number of callback arguments must match dispatcher dimension"); m_callback_map[make_key()] = std::move(cb); } template void erase() { static_assert(sizeof...(D) == sizeof...(B), "Number of callback arguments must match dispatcher dimension"); m_callback_map.erase(make_key()); } inline return_type dispatch(B&... args, T&... udargs) const { key_type k = {{std::type_index(typeid(args))...}}; auto it = m_callback_map.find(k); if (it == m_callback_map.end()) { XTL_THROW(std::runtime_error, "callback not found"); } return (it->second)(args..., udargs...); } }; /************************* * basic_fast_dispatcher * *************************/ #define XTL_IMPLEMENT_INDEXABLE_CLASS() \ static std::size_t& get_class_static_index()\ { \ static std::size_t index = SIZE_MAX; \ return index; \ } \ virtual std::size_t get_class_index() const \ { \ return get_class_static_index(); \ } namespace detail { template class recursive_container_impl : private std::vector { public: using base_type = std::vector; using base_type::base_type; using base_type::operator[]; using base_type::size; using base_type::resize; }; template class recursive_container : public recursive_container_impl> { }; template class recursive_container : public recursive_container_impl { }; } template < class type_list, class return_type, class undispatched_type_list, class callback_type > class basic_fast_dispatcher; template < class return_type, class callback_type, class... B, class... T > class basic_fast_dispatcher, return_type, mpl::vector, callback_type> { private: static constexpr std::size_t nb_args = sizeof...(B); using storage_type = detail::recursive_container; using index_type = std::array; using index_ref_type = std::array, nb_args>; storage_type m_callbacks; std::size_t m_next_index; template void resize_container(C& c, const index_ref_type& index) { std::size_t& idx = index[I]; if (idx == SIZE_MAX) { c.resize(++m_next_index); idx = c.size() - 1u; } else if(c.size() <= idx) { c.resize(idx + 1u); } } template std::enable_if_t insert_impl(callback_type&& cb, C& c, const index_ref_type& index) { resize_container(c, index); c[index[I]] = std::move(cb); } template std::enable_if_t insert_impl(callback_type&& cb, C& c, const index_ref_type& index) { resize_container(c, index); insert_impl(std::move(cb), c[index[I]], index); } template void check_size(C& c, const index_type& index) const { if (index[I] >= c.size()) { XTL_THROW(std::runtime_error, "callback not found"); } } template std::enable_if_t dispatch_impl(C& c, const index_type& index, B&... args, T&... udargs) const { check_size(c, index); return c[index[I]](args..., udargs...); } template std::enable_if_t dispatch_impl(C& c, const index_type& index, B&... args, T&... udargs) const { check_size(c, index); return dispatch_impl(c[index[I]], index, args..., udargs...); } public: inline basic_fast_dispatcher() : m_next_index(0) { } template void insert(callback_type&& cb) { static_assert(sizeof...(D) == sizeof...(B), "Number of callback arguments must match dispatcher dimension"); index_ref_type index = {{std::ref(D::get_class_static_index())...}}; insert_impl<0>(std::move(cb), m_callbacks, index); } inline return_type dispatch(B&... args, T&... udargs) const { index_type index = {{args.get_class_index()...}}; return dispatch_impl<0>(m_callbacks, index, args..., udargs...); } }; /****************************** * dynamic and static casters * ******************************/ template struct static_caster { static T& cast(F& f) { return static_cast(f); } }; template struct dynamic_caster { static T& cast(F& f) { return dynamic_cast(f); } }; /********************** * functor_dispatcher * **********************/ template < class type_list, class return_type, class undispatched_type = mpl::vector<>, template class casting_policy = dynamic_caster, template class dispatcher = basic_dispatcher > class functor_dispatcher; template < class return_type, template class casting_policy, template class dispatcher, class... B, class... T > class functor_dispatcher, return_type, mpl::vector, casting_policy, dispatcher> { private: using functor_type = std::function; using backend = dispatcher, return_type, mpl::vector, functor_type>; backend m_backend; public: template void insert(const Fun& fun) { functor_type f([fun](B&... args, T&... udargs) -> return_type { return fun(casting_policy::cast(args)..., udargs...); }); m_backend.template insert(std::move(f)); } template void erase() { m_backend.template erase(); } inline return_type dispatch(B&... args, T&... udargs) const { return m_backend.dispatch(args..., udargs...); } }; } #endif