mp/examples/et/5_currying.cpp

222 lines
5.6 KiB
C++
Raw Normal View History

2019-08-14 14:40:58 +00:00
#include <cstdio>
#include <tuple>
/* **** */
/* tags */
namespace tag {
struct Argument;
struct Add;
struct Mul;
struct Minus;
}
/* ********** */
/* expression */
template<typename Op, typename... Operands>
struct Expr {
std::tuple<Operands...> operands;
Expr(Operands... operands): operands{operands...} {}
};
template<std::size_t I>
struct Argument {};
namespace arg {
Argument<0> _0;
Argument<1> _1;
Argument<2> _2;
Argument<3> _3;
// ...
}
/* *********** */
/* type traits */
namespace impl {
template<typename T> struct IsExpr: std::false_type {};
template<typename Op, typename... Operands>
struct IsExpr<Expr<Op, Operands...>>: std::true_type {};
template<std::size_t I>
struct IsExpr<Argument<I>>: std::true_type {};
}
template<typename T>
constexpr bool IsExpr = impl::IsExpr<T>::value;
template<
typename LHS, typename RHS,
std::enable_if_t<IsExpr<LHS> && IsExpr<RHS>>* = nullptr
>
auto operator+(LHS lhs, RHS rhs) {
return Expr<tag::Add, LHS, RHS>{lhs, rhs};
}
template<
typename LHS, typename RHS,
std::enable_if_t<IsExpr<LHS> && IsExpr<RHS>>* = nullptr
>
auto operator*(LHS lhs, RHS rhs) {
return Expr<tag::Mul, LHS, RHS>{lhs, rhs};
}
template<
typename RHS,
std::enable_if_t<IsExpr<RHS>>* = nullptr
>
auto operator-(RHS rhs) {
return Expr<tag::Minus, RHS>{rhs};
}
/* ********** */
/* evaluators */
template<typename> struct EvaluatorElem;
template<>
struct EvaluatorElem<tag::Argument> {
template<typename T>
static auto eval(T value) { return value; }
};
template<>
struct EvaluatorElem<tag::Add> {
template<typename T>
static auto eval(T lhs, T rhs) { return lhs + rhs; }
};
template<>
struct EvaluatorElem<tag::Mul> {
template<typename T>
static auto eval(T lhs, T rhs) { return lhs * rhs; }
};
template<>
struct EvaluatorElem<tag::Minus> {
template<typename T>
static auto eval(T rhs) { return -rhs; }
};
/* *** */
namespace impl {
template<typename> struct GetExprOp;
template<typename Op, typename... Operands>
struct GetExprOp<Expr<Op, Operands...>> {
using type = Op;
};
}
template<typename E>
using GetExprOp = typename impl::GetExprOp<E>::type;
namespace impl {
template<typename...> struct MaxArgument;
template<typename Argument, typename... Arguments>
struct MaxArgument<Argument, Arguments...>:
std::integral_constant<std::size_t, MaxArgument<Arguments...>::value> {};
template<std::size_t I, typename... Arguments>
struct MaxArgument<Argument<I>, Arguments...> {
static constexpr std::size_t M = MaxArgument<Arguments...>::value;
static constexpr std::size_t value = I>M? I:M;
};
template<typename Op, typename... Operands, typename... Arguments>
struct MaxArgument<Expr<Op, Operands...>, Arguments...> {
static constexpr std::size_t M0 = MaxArgument<Arguments...>::value;
static constexpr std::size_t M1 = MaxArgument<Operands...>::value;
static constexpr std::size_t value = M0>M1? M0:M1;
};
template<> struct MaxArgument<>: std::integral_constant<std::size_t, 0> {};
}
template<typename... Arguments>
constexpr std::size_t MaxArgument = impl::MaxArgument<Arguments...>::value;
template<typename Expression, template<typename> class EvaluatorTemplate, typename BoundArgs = std::tuple<>, bool Impl = false>
struct Evaluable {
using Evaluator = EvaluatorTemplate<GetExprOp<Expression>>;
Expression expr;
BoundArgs boundArgs;
static constexpr std::size_t arity = MaxArgument<Expression> + 1 - std::tuple_size<BoundArgs>{};
template<typename... Arguments, std::enable_if_t<sizeof...(Arguments) >= arity>* = nullptr>
auto operator()(Arguments... arguments) {
static_assert(Impl || sizeof...(Arguments) <= arity, "too many arguments");
using IndexSequence = std::make_index_sequence<std::tuple_size<BoundArgs>{}>;
return evalBound(IndexSequence{}, arguments...);
}
template<typename... Arguments, std::enable_if_t<(sizeof...(Arguments) < arity)>* = nullptr>
auto operator()(Arguments... arguments) {
auto newBoundArgs = std::tuple_cat(boundArgs, std::make_tuple(arguments...));
return Evaluable<Expression, EvaluatorTemplate, decltype(newBoundArgs)>{expr, newBoundArgs};
}
private:
template<std::size_t... Is, typename... Arguments>
auto evalBound(std::index_sequence<Is...>, Arguments... arguments) {
using IndexSequence = std::make_index_sequence<std::tuple_size<decltype(expr.operands)>{}>;
return eval(IndexSequence{}, std::get<Is>(boundArgs)..., arguments...);
}
template<typename... Arguments, std::size_t... Is>
auto eval(std::index_sequence<Is...>, Arguments... arguments) {
using IndexSequence = std::index_sequence<Is...>;
return Evaluator::eval(std::get<Is>(resolveOperands(IndexSequence{}, expr.operands, arguments...))...);
}
template<std::size_t... Is, typename T, typename... Arguments>
auto resolveOperands(std::index_sequence<Is...>, T tuple, Arguments... arguments) {
return std::make_tuple(resolveOperand(std::get<Is>(tuple), arguments...)...);
}
template<std::size_t I, typename... Arguments>
auto resolveOperand(Argument<I>, Arguments... arguments) {
return std::get<I>(std::make_tuple(arguments...));
}
template<typename... Ts, typename... Arguments>
auto resolveOperand(Expr<Ts...> expr, Arguments... arguments) {
return Evaluable<Expr<Ts...>, EvaluatorTemplate, std::tuple<>, true>{expr, {}}(arguments...);
}
};
template<template<typename> class Evaluator, typename Expr>
auto bind(Expr expr) {
return Evaluable<Expr, Evaluator>{expr, {}};
}
int main() {
int volatile v0 = 3, v1 = 5, v2 = 7, v3 = 9;
int result;
using namespace arg;
auto expr = _1 * -(_2 + _0) + _3 * _0;
auto fElem = bind<EvaluatorElem>(expr);
auto fCurry = fElem(v0);
result = fCurry(v1)(v2)(v3);
std::printf("result: %d\n", result);
}