|
#pragma once |
|
|
|
// Copyright plasma-effect 2015 |
|
// Distributed under the Boost Software License, Version 1.0. |
|
// (See http://www.boost.org/LICENSE_1_0.txt) |
|
|
|
#include<tuple> |
|
#include<typeinfo> |
|
#include<utility> |
|
#include<memory> |
|
#include<functional> |
|
#include<vector> |
|
|
|
namespace plasma |
|
{ |
|
namespace algebraic_data_type |
|
{ |
|
struct recursion_tag {}; |
|
namespace detail |
|
{ |
|
bool all_and(bool f) |
|
{ |
|
return f; |
|
} |
|
template<class... Ts>bool all_and(bool f, Ts... args) |
|
{ |
|
return f&&all_and(args...); |
|
} |
|
} |
|
|
|
template<class... Types>class data_type |
|
{ |
|
typedef data_type<Types...> this_type; |
|
typedef std::tuple<Types...> template_parameter; |
|
|
|
struct base_type |
|
{ |
|
virtual ~base_type() {} |
|
base_type() = default; |
|
base_type(base_type const&) = default; |
|
base_type(base_type&&) = default; |
|
base_type& operator=(base_type const&) = default; |
|
base_type& operator=(base_type&&) = default; |
|
|
|
virtual std::type_info const& type() |
|
{ |
|
return typeid(*this); |
|
} |
|
virtual std::unique_ptr<base_type> clone()const = 0; |
|
virtual bool equal(base_type& rhs) = 0; |
|
}; |
|
|
|
template<class T, class = void>struct ptr_type |
|
{ |
|
T ptr_; |
|
typedef T value_type; |
|
value_type get_value()const |
|
{ |
|
return ptr_; |
|
} |
|
|
|
ptr_type(value_type const& arg) :ptr_(arg) {} |
|
ptr_type(ptr_type const&) = delete; |
|
ptr_type(ptr_type&&) = default; |
|
ptr_type& operator=(ptr_type const&) = delete; |
|
ptr_type& operator=(ptr_type&&) = default; |
|
}; |
|
template<class T>struct ptr_type<T, std::enable_if_t<std::is_same<T, recursion_tag>::value>> |
|
{ |
|
std::unique_ptr<this_type> ptr_; |
|
typedef this_type value_type; |
|
value_type get_value()const |
|
{ |
|
return *ptr_; |
|
} |
|
|
|
ptr_type(value_type const& arg) :ptr_(std::make_unique<value_type>(arg)) {} |
|
ptr_type(ptr_type const&) = delete; |
|
ptr_type(ptr_type&&) = default; |
|
ptr_type& operator=(ptr_type const&) = delete; |
|
ptr_type& operator=(ptr_type&&) = default; |
|
}; |
|
template<class T>using true_t = typename ptr_type<T>::value_type; |
|
|
|
template<std::size_t I, class T>struct inside_data :base_type |
|
{ |
|
std::tuple<ptr_type<T>> datas; |
|
std::unique_ptr<base_type> clone()const |
|
{ |
|
return std::make_unique<inside_data<I, T>>(std::make_tuple(ptr_type<T>(std::get<0>(datas).get_value()))); |
|
} |
|
typedef std::tuple<true_t<T>> argument_type; |
|
|
|
bool equal(base_type& rhs) |
|
{ |
|
if (this->type() != rhs.type()) |
|
{ |
|
return false; |
|
} |
|
inside_data<I, T>& p = dynamic_cast<inside_data<I, T>&>(rhs); |
|
return std::get<0>(datas).get_value() == std::get<0>(p.datas).get_value(); |
|
} |
|
|
|
inside_data(std::tuple<ptr_type<T>>&& arg) :datas(std::move(arg)) {} |
|
inside_data(true_t<T>const& arg) :datas(ptr_type<T>(arg)){} |
|
inside_data(inside_data const&) = delete; |
|
inside_data(inside_data&&) = default; |
|
inside_data& operator=(inside_data const&) = delete; |
|
inside_data& operator=(inside_data&&) = default; |
|
}; |
|
template<std::size_t I, class... Ts>struct inside_data<I, std::tuple<Ts...>> :base_type |
|
{ |
|
std::tuple<ptr_type<Ts>...> datas; |
|
std::unique_ptr<base_type> clone()const |
|
{ |
|
return clone_i(std::make_index_sequence<sizeof...(Ts)>()); |
|
} |
|
template<std::size_t... Us>auto clone_i(std::index_sequence<Us...>)const |
|
{ |
|
return std::make_unique<inside_data<I, std::tuple<Ts...>>>(std::make_tuple((ptr_type<Ts>(std::get<Us>(datas).get_value()))...)); |
|
} |
|
|
|
bool equal(base_type& rhs) |
|
{ |
|
return this->type() == rhs.type() && equal_i(dynamic_cast<inside_data<I, std::tuple<Ts...>>&>(rhs), std::make_index_sequence<sizeof...(Ts)>()); |
|
} |
|
|
|
template<std::size_t... Is>bool equal_i(inside_data<I, std::tuple<Ts...>>& rhs, std::index_sequence<Is...>) |
|
{ |
|
return detail::all_and((std::get<Is>(datas).get_value() == std::get<Is>(rhs.datas).get_value())...); |
|
} |
|
|
|
inside_data(std::tuple<ptr_type<Ts>...>&& arg) :datas(std::move(arg)) {} |
|
inside_data(true_t<Ts>const&... arg) :datas(ptr_type<Ts>(arg)...) {} |
|
inside_data(inside_data const&) = delete; |
|
inside_data(inside_data&&) = default; |
|
inside_data& operator=(inside_data const&) = delete; |
|
inside_data& operator=(inside_data&&) = default; |
|
}; |
|
template<std::size_t I>struct inside_data<I, void> :base_type |
|
{ |
|
std::unique_ptr<base_type> clone()const |
|
{ |
|
return std::make_unique<inside_data<I, void>>(); |
|
} |
|
|
|
inside_data() = default; |
|
inside_data(inside_data const&) = delete; |
|
inside_data(inside_data&&) = default; |
|
inside_data& operator=(inside_data const&) = delete; |
|
inside_data& operator=(inside_data&&) = default; |
|
|
|
bool equal(base_type& rhs) |
|
{ |
|
return this->type() == rhs.type(); |
|
} |
|
}; |
|
|
|
template<std::size_t I, class T>struct make_inside_data_t |
|
{ |
|
static std::unique_ptr<base_type> run(true_t<T>const& arg) |
|
{ |
|
return std::make_unique<inside_data<I, T>>(arg); |
|
} |
|
}; |
|
template<std::size_t I, class... Ts>struct make_inside_data_t<I, std::tuple<Ts...>> |
|
{ |
|
static std::unique_ptr<base_type> run(true_t<Ts>const&...args) |
|
{ |
|
return std::make_unique<inside_data<I, std::tuple<Ts...>>>(args...); |
|
} |
|
}; |
|
template<std::size_t I>struct make_inside_data_t<I, void> |
|
{ |
|
static std::unique_ptr<base_type> run() |
|
{ |
|
return std::make_unique<inside_data<I, void>>(); |
|
} |
|
}; |
|
|
|
template<std::size_t I, class T>struct make_data_type_t |
|
{ |
|
static this_type run(true_t<T>const& arg) |
|
{ |
|
this_type ret{}; |
|
ret.this_ptr_ = make_inside_data_t<I, T>::run(arg); |
|
return ret; |
|
} |
|
}; |
|
template<std::size_t I, class... Ts>struct make_data_type_t<I, std::tuple<Ts...>> |
|
{ |
|
static this_type run(true_t<Ts> const&... args) |
|
{ |
|
this_type ret{}; |
|
ret.this_ptr_ = make_inside_data_t<I, std::tuple<Ts...>>::run(args...); |
|
return ret; |
|
} |
|
}; |
|
template<std::size_t I>struct make_data_type_t<I, void> |
|
{ |
|
static this_type run() |
|
{ |
|
this_type ret{}; |
|
ret.this_ptr_ = make_inside_data_t<I, void>::run(); |
|
return ret; |
|
} |
|
}; |
|
|
|
template<class Return, class T>struct function_call_t |
|
{ |
|
template<std::size_t I>static Return run(std::function<Return(true_t<T>)>const& func, inside_data<I, T> const& arg) |
|
{ |
|
return func(std::get<0>(arg.datas).get_value()); |
|
} |
|
typedef std::function<Return(true_t<T>)> function_t; |
|
}; |
|
template<class Return, class... Ts>struct function_call_t<Return, std::tuple<Ts...>> |
|
{ |
|
template<std::size_t I>static Return run(std::function<Return(true_t<Ts>...)>const& func, inside_data<I, std::tuple<Ts...>> const& arg) |
|
{ |
|
return run_i(func, arg, std::make_index_sequence<sizeof...(Ts)>()); |
|
} |
|
template<std::size_t I,std::size_t... Is>static Return run_i(std::function<Return(true_t<Ts>...)>const& func, inside_data<I, std::tuple<Ts...>> const& arg, std::index_sequence<Is...>) |
|
{ |
|
return func(std::get<Is>(arg.datas).get_value()...); |
|
} |
|
typedef std::function<Return(true_t<Ts>...)> function_t; |
|
}; |
|
template<class Return>struct function_call_t<Return, void> |
|
{ |
|
template<std::size_t I>static Return run(std::function<Return(void)> const& func, inside_data<I, void>const&) |
|
{ |
|
return func(); |
|
} |
|
typedef std::function<Return(void)> function_t; |
|
}; |
|
|
|
template<class Return, class T>struct recursion_call_t |
|
{ |
|
template<std::size_t I>static Return run( |
|
std::function<Return(std::function<Return(this_type)>,true_t<T>)>const& func, |
|
std::function<Return(this_type)> const& recur, |
|
inside_data<I, T> const& arg) |
|
{ |
|
return func(recur, std::get<0>(arg.datas).get_value()); |
|
} |
|
typedef std::function<Return(std::function<Return(this_type)>, true_t<T>)> function_t; |
|
}; |
|
template<class Return, class... Ts>struct recursion_call_t<Return, std::tuple<Ts...>> |
|
{ |
|
template<std::size_t I>static Return run( |
|
std::function<Return(std::function<Return(this_type)>, true_t<Ts>...)>const& func, |
|
std::function<Return(this_type)>const& recur, |
|
inside_data<I, std::tuple<Ts...>> const& arg) |
|
{ |
|
return run_i(func, recur, arg, std::make_index_sequence<sizeof...(Ts)>()); |
|
} |
|
template<std::size_t I, std::size_t... Is>static Return run_i( |
|
std::function<Return(std::function<Return(this_type)>, true_t<Ts>...)>const& func, |
|
std::function<Return(this_type)>const& recur, |
|
inside_data<I, std::tuple<Ts...>> const& arg, |
|
std::index_sequence<Is...>) |
|
{ |
|
return func(recur, std::get<Is>(arg.datas).get_value()...); |
|
} |
|
typedef std::function<Return(std::function<Return(this_type)>, true_t<Ts>...)> function_t; |
|
}; |
|
template<class Return>struct recursion_call_t<Return, void> |
|
{ |
|
template<std::size_t I>static Return run( |
|
std::function<Return(std::function<Return(this_type)>)> const& func, |
|
std::function<Return(this_type)> const& recur, |
|
inside_data<I, void>const&) |
|
{ |
|
return func(recur); |
|
} |
|
typedef std::function<Return(std::function<Return(this_type)>)> function_t; |
|
}; |
|
|
|
template<std::size_t I>using get_inside_type = inside_data<I, std::tuple_element_t<I, template_parameter>>; |
|
|
|
|
|
std::unique_ptr<base_type> this_ptr_; |
|
|
|
data_type() = default; |
|
public: |
|
data_type(data_type const& arg) :this_ptr_(arg.this_ptr_->clone()) {} |
|
data_type(data_type&&) = default; |
|
data_type& operator=(data_type const& arg) |
|
{ |
|
this_ptr_ = arg.this_ptr_->clone(); |
|
return *this; |
|
} |
|
data_type& operator=(data_type&&) = default; |
|
|
|
template<std::size_t I>static auto make_instance_function() |
|
{ |
|
return make_data_type_t<I, std::tuple_element_t<I, template_parameter>>::run; |
|
} |
|
|
|
template<class Return>struct pattern_match_t |
|
{ |
|
std::tuple<typename function_call_t<Return, Types>::function_t...> funcs; |
|
|
|
Return operator()(this_type const& arg)const |
|
{ |
|
return run<0>(arg); |
|
} |
|
template<std::size_t I>std::enable_if_t<I == sizeof...(Types), Return> run(this_type const&)const |
|
{ |
|
return Return(); |
|
} |
|
template<std::size_t I>std::enable_if_t<I != sizeof...(Types), Return> run(this_type const& arg)const |
|
{ |
|
return (arg.this_ptr_->type() == typeid(get_inside_type<I>) ? |
|
function_call_t<Return, std::tuple_element_t<I, template_parameter>>::run( |
|
std::get<I>(funcs), |
|
dynamic_cast<get_inside_type<I> const&>(*arg.this_ptr_)) : |
|
run<I + 1>(arg)); |
|
} |
|
}; |
|
template<class Return>static pattern_match_t<Return>make_pattern(typename function_call_t<Return, Types>::function_t const&... funcs) |
|
{ |
|
return pattern_match_t<Return>{std::make_tuple(funcs...)}; |
|
} |
|
|
|
template<class Return>struct recursion_pattern_match_t |
|
{ |
|
std::tuple<typename recursion_call_t<Return, Types>::function_t...> funcs; |
|
|
|
Return operator()(this_type const& arg)const |
|
{ |
|
return run<0>(arg); |
|
} |
|
template<std::size_t I>std::enable_if_t<I == sizeof...(Types), Return> run(this_type const&)const |
|
{ |
|
return Return(); |
|
} |
|
template<std::size_t I>std::enable_if_t<I != sizeof...(Types), Return> run(this_type const& arg)const |
|
{ |
|
return (arg.this_ptr_->type() == typeid(get_inside_type<I>) ? |
|
recursion_call_t<Return, std::tuple_element_t<I, template_parameter>>::run( |
|
std::get<I>(funcs), |
|
std::ref(*this), |
|
dynamic_cast<get_inside_type<I> const&>(*arg.this_ptr_)) : |
|
run<I + 1>(arg)); |
|
} |
|
}; |
|
template<class Return>static recursion_pattern_match_t<Return>make_recursion(typename recursion_call_t<Return, Types>::function_t const&... funcs) |
|
{ |
|
return recursion_pattern_match_t<Return>{std::make_tuple(funcs...)}; |
|
} |
|
|
|
template<class Return>struct memoization_pattern_match_t |
|
{ |
|
std::tuple<typename function_call_t<Return, Types>::function_t...> funcs; |
|
std::shared_ptr<std::vector<std::pair<this_type, Return>>> vec; |
|
|
|
Return operator()(this_type const& arg)const |
|
{ |
|
for (auto const& v : *vec) |
|
{ |
|
if (v.first == arg) |
|
{ |
|
return v.second; |
|
} |
|
} |
|
auto ret = run<0>(arg); |
|
vec->emplace_back(arg, ret); |
|
return ret; |
|
} |
|
template<std::size_t I>std::enable_if_t<I == sizeof...(Types), Return> run(this_type const&)const |
|
{ |
|
return Return(); |
|
} |
|
template<std::size_t I>std::enable_if_t<I != sizeof...(Types), Return> run(this_type const& arg)const |
|
{ |
|
return (arg.this_ptr_->type() == typeid(get_inside_type<I>) ? |
|
function_call_t<Return, std::tuple_element_t<I, template_parameter>>::run( |
|
std::get<I>(funcs), |
|
dynamic_cast<get_inside_type<I> const&>(*arg.this_ptr_)) : |
|
run<I + 1>(arg)); |
|
} |
|
}; |
|
template<class Return>static auto make_memoization_pattern(typename function_call_t<Return, Types>::function_t const&... funcs) |
|
{ |
|
return memoization_pattern_match_t<Return>{std::make_tuple(funcs...), std::make_shared<std::vector<std::pair<this_type, Return>>>()}; |
|
} |
|
|
|
template<class Return>struct memoization_recursion_pattern_match_t |
|
{ |
|
std::tuple<typename recursion_call_t<Return, Types>::function_t...> funcs; |
|
std::shared_ptr<std::vector<std::pair<this_type, Return>>> vec; |
|
|
|
Return operator()(this_type const& arg)const |
|
{ |
|
for (auto const& v : *vec) |
|
{ |
|
if (v.first == arg) |
|
{ |
|
return v.second; |
|
} |
|
} |
|
auto ret = run<0>(arg); |
|
vec->emplace_back(arg, ret); |
|
return ret; |
|
} |
|
template<std::size_t I>std::enable_if_t<I == sizeof...(Types), Return> run(this_type const&)const |
|
{ |
|
return Return(); |
|
} |
|
template<std::size_t I>std::enable_if_t<I != sizeof...(Types), Return> run(this_type const& arg)const |
|
{ |
|
return (arg.this_ptr_->type() == typeid(get_inside_type<I>) ? |
|
recursion_call_t<Return, std::tuple_element_t<I, template_parameter>>::run( |
|
std::get<I>(funcs), |
|
std::ref(*this), |
|
dynamic_cast<get_inside_type<I> const&>(*arg.this_ptr_)) : |
|
run<I + 1>(arg)); |
|
} |
|
}; |
|
template<class Return>static auto make_memoization_recursion(typename recursion_call_t<Return, Types>::function_t const&... funcs) |
|
{ |
|
return memoization_recursion_pattern_match_t<Return>{std::make_tuple(funcs...), std::make_shared<std::vector<std::pair<this_type, Return>>>()}; |
|
} |
|
|
|
|
|
bool operator==(this_type const& rhs)const |
|
{ |
|
return this_ptr_->equal(*rhs.this_ptr_); |
|
} |
|
bool operator!=(this_type const& rhs)const |
|
{ |
|
return !this_ptr_->equal(*rhs.this_ptr_); |
|
} |
|
}; |
|
} |
|
} |