222 lines
5.6 KiB
C++
222 lines
5.6 KiB
C++
#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);
|
|
}
|