mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 11:30:18 +00:00
423 lines
13 KiB
C++
423 lines
13 KiB
C++
/***************************************************************************
|
|
* 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 <array>
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <typeindex>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
|
|
#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 <class lhs_type, class rhs_type>
|
|
static return_type invoke_executor(lhs_type& lhs,
|
|
rhs_type& rhs,
|
|
executor& exec,
|
|
std::false_type)
|
|
{
|
|
return exec.run(lhs, rhs);
|
|
}
|
|
|
|
template <class lhs_type, class rhs_type>
|
|
static return_type invoke_executor(lhs_type& lhs,
|
|
rhs_type& rhs,
|
|
executor& exec,
|
|
std::true_type)
|
|
{
|
|
return exec.run(rhs, lhs);
|
|
}
|
|
|
|
template <class lhs_type>
|
|
static return_type dispatch_rhs(lhs_type& lhs,
|
|
base_rhs& rhs,
|
|
executor& exec,
|
|
mpl::vector<>)
|
|
{
|
|
return exec.on_error(lhs, rhs);
|
|
}
|
|
|
|
template <class lhs_type, class T, class... U>
|
|
static return_type dispatch_rhs(lhs_type& lhs,
|
|
base_rhs& rhs,
|
|
executor& exec,
|
|
mpl::vector<T, U...>)
|
|
{
|
|
if (T* p = dynamic_cast<T*>(&rhs))
|
|
{
|
|
constexpr std::size_t lhs_index = mpl::index_of<lhs_type_list, lhs_type>::value;
|
|
constexpr std::size_t rhs_index = mpl::index_of<rhs_type_list, T>::value;
|
|
|
|
using invoke_flag = std::integral_constant<bool,
|
|
std::is_same<symmetric, symmetric_dispatch>::value && (rhs_index < lhs_index)>;
|
|
return invoke_executor(lhs, *p, exec, invoke_flag());
|
|
}
|
|
return dispatch_rhs(lhs, rhs, exec, mpl::vector<U...>());
|
|
}
|
|
|
|
static return_type dispatch_lhs(base_lhs& lhs,
|
|
base_rhs& rhs,
|
|
executor& exec,
|
|
mpl::vector<>)
|
|
{
|
|
return exec.on_error(lhs, rhs);
|
|
}
|
|
|
|
template <class T, class... U>
|
|
static return_type dispatch_lhs(base_lhs& lhs,
|
|
base_rhs& rhs,
|
|
executor& exec,
|
|
mpl::vector<T, U...>)
|
|
{
|
|
if (T* p = dynamic_cast<T*>(&lhs))
|
|
{
|
|
return dispatch_rhs(*p, rhs, exec, rhs_type_list());
|
|
}
|
|
return dispatch_lhs(lhs, rhs, exec, mpl::vector<U...>());
|
|
}
|
|
|
|
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<mpl::vector<B...>, return_type, mpl::vector<T...>, callback_type>
|
|
{
|
|
private:
|
|
|
|
using key_type = std::array<std::type_index, sizeof...(B)>;
|
|
using map_type = std::map<key_type, callback_type>;
|
|
map_type m_callback_map;
|
|
|
|
template <class... U>
|
|
key_type make_key() const
|
|
{
|
|
return {{std::type_index(typeid(U))...}};
|
|
}
|
|
|
|
public:
|
|
|
|
template <class... D>
|
|
void insert(callback_type&& cb)
|
|
{
|
|
static_assert(sizeof...(D) == sizeof...(B),
|
|
"Number of callback arguments must match dispatcher dimension");
|
|
m_callback_map[make_key<D...>()] = std::move(cb);
|
|
}
|
|
|
|
template <class... D>
|
|
void erase()
|
|
{
|
|
static_assert(sizeof...(D) == sizeof...(B),
|
|
"Number of callback arguments must match dispatcher dimension");
|
|
m_callback_map.erase(make_key<D...>());
|
|
}
|
|
|
|
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 T>
|
|
class recursive_container_impl : private std::vector<T>
|
|
{
|
|
public:
|
|
|
|
using base_type = std::vector<T>;
|
|
|
|
using base_type::base_type;
|
|
using base_type::operator[];
|
|
using base_type::size;
|
|
using base_type::resize;
|
|
};
|
|
|
|
template <class callback_type, std::size_t level>
|
|
class recursive_container
|
|
: public recursive_container_impl<recursive_container<callback_type, level-1>>
|
|
{
|
|
};
|
|
|
|
template <class callback>
|
|
class recursive_container<callback, 0>
|
|
: public recursive_container_impl<callback>
|
|
{
|
|
};
|
|
}
|
|
|
|
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<mpl::vector<B...>, return_type, mpl::vector<T...>, callback_type>
|
|
{
|
|
private:
|
|
|
|
static constexpr std::size_t nb_args = sizeof...(B);
|
|
|
|
using storage_type = detail::recursive_container<callback_type, sizeof...(B) - 1>;
|
|
using index_type = std::array<std::size_t, nb_args>;
|
|
using index_ref_type = std::array<std::reference_wrapper<std::size_t>, nb_args>;
|
|
|
|
storage_type m_callbacks;
|
|
std::size_t m_next_index;
|
|
|
|
template <std::size_t I, class C>
|
|
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::size_t I, class C>
|
|
std::enable_if_t<I + 1 == nb_args>
|
|
insert_impl(callback_type&& cb, C& c, const index_ref_type& index)
|
|
{
|
|
resize_container<I>(c, index);
|
|
c[index[I]] = std::move(cb);
|
|
}
|
|
|
|
template <std::size_t I, class C>
|
|
std::enable_if_t<I + 1 != nb_args>
|
|
insert_impl(callback_type&& cb, C& c, const index_ref_type& index)
|
|
{
|
|
resize_container<I>(c, index);
|
|
insert_impl<I+1>(std::move(cb), c[index[I]], index);
|
|
}
|
|
|
|
template <std::size_t I, class C>
|
|
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::size_t I, class C>
|
|
std::enable_if_t<I + 1 == nb_args, return_type>
|
|
dispatch_impl(C& c, const index_type& index, B&... args, T&... udargs) const
|
|
{
|
|
check_size<I>(c, index);
|
|
return c[index[I]](args..., udargs...);
|
|
}
|
|
|
|
template <std::size_t I, class C>
|
|
std::enable_if_t<I + 1 != nb_args, return_type>
|
|
dispatch_impl(C& c, const index_type& index, B&... args, T&... udargs) const
|
|
{
|
|
check_size<I>(c, index);
|
|
return dispatch_impl<I+1>(c[index[I]], index, args..., udargs...);
|
|
}
|
|
|
|
public:
|
|
|
|
inline basic_fast_dispatcher()
|
|
: m_next_index(0)
|
|
{
|
|
}
|
|
|
|
template <class... D>
|
|
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 <class T, class F>
|
|
struct static_caster
|
|
{
|
|
static T& cast(F& f)
|
|
{
|
|
return static_cast<T&>(f);
|
|
}
|
|
};
|
|
|
|
template <class T, class F>
|
|
struct dynamic_caster
|
|
{
|
|
static T& cast(F& f)
|
|
{
|
|
return dynamic_cast<T&>(f);
|
|
}
|
|
};
|
|
|
|
/**********************
|
|
* functor_dispatcher *
|
|
**********************/
|
|
|
|
template
|
|
<
|
|
class type_list,
|
|
class return_type,
|
|
class undispatched_type = mpl::vector<>,
|
|
template <class, class> class casting_policy = dynamic_caster,
|
|
template <class, class, class, class> class dispatcher = basic_dispatcher
|
|
>
|
|
class functor_dispatcher;
|
|
|
|
template
|
|
<
|
|
class return_type,
|
|
template <class, class> class casting_policy,
|
|
template <class, class, class, class> class dispatcher,
|
|
class... B,
|
|
class... T
|
|
>
|
|
class functor_dispatcher<mpl::vector<B...>, return_type, mpl::vector<T...>, casting_policy, dispatcher>
|
|
{
|
|
private:
|
|
|
|
using functor_type = std::function<return_type (B&..., T&...)>;
|
|
using backend = dispatcher<mpl::vector<B...>,
|
|
return_type,
|
|
mpl::vector<T...>,
|
|
functor_type>;
|
|
backend m_backend;
|
|
|
|
public:
|
|
|
|
template <class... D, class Fun>
|
|
void insert(const Fun& fun)
|
|
{
|
|
functor_type f([fun](B&... args, T&... udargs) -> return_type
|
|
{
|
|
return fun(casting_policy<D&, B&>::cast(args)..., udargs...);
|
|
});
|
|
m_backend.template insert<D...>(std::move(f));
|
|
}
|
|
|
|
template <class... D>
|
|
void erase()
|
|
{
|
|
m_backend.template erase<D...>();
|
|
}
|
|
|
|
inline return_type dispatch(B&... args, T&... udargs) const
|
|
{
|
|
return m_backend.dispatch(args..., udargs...);
|
|
}
|
|
};
|
|
}
|
|
|
|
#endif
|