253 lines
5.9 KiB
C++
253 lines
5.9 KiB
C++
|
#include <algorithm>
|
||
|
#include <cstdio>
|
||
|
#include <tuple>
|
||
|
#include <vector>
|
||
|
|
||
|
/* **** */
|
||
|
/* tags */
|
||
|
|
||
|
namespace tag {
|
||
|
|
||
|
struct Argument;
|
||
|
|
||
|
struct Add;
|
||
|
struct Mul;
|
||
|
struct Minus;
|
||
|
struct Less;
|
||
|
|
||
|
}
|
||
|
|
||
|
/* ********** */
|
||
|
/* 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};
|
||
|
}
|
||
|
|
||
|
template<
|
||
|
typename LHS, typename RHS,
|
||
|
std::enable_if_t<IsExpr<LHS> && IsExpr<RHS>>* = nullptr
|
||
|
>
|
||
|
auto operator<(LHS lhs, RHS rhs) {
|
||
|
return Expr<tag::Less, LHS, RHS>{lhs, 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; }
|
||
|
};
|
||
|
|
||
|
template<>
|
||
|
struct EvaluatorElem<tag::Less> {
|
||
|
template<typename T>
|
||
|
static auto eval(T lhs, T rhs) { return lhs < 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, bool Impl = false>
|
||
|
struct Evaluable {
|
||
|
using Evaluator = EvaluatorTemplate<GetExprOp<Expression>>;
|
||
|
|
||
|
Expression expr;
|
||
|
|
||
|
static constexpr std::size_t arity = MaxArgument<Expression> + 1;
|
||
|
|
||
|
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<decltype(expr.operands)>{}>;
|
||
|
return eval(IndexSequence{}, arguments...);
|
||
|
}
|
||
|
|
||
|
template<typename... Arguments, std::enable_if_t<(sizeof...(Arguments) < arity)>* = nullptr>
|
||
|
auto operator()(Arguments...) {
|
||
|
static_assert(sizeof...(Arguments) >= arity, "too few arguments");
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
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, true>{expr}(arguments...);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template<template<typename> class Evaluator, typename Expr>
|
||
|
auto bind(Expr expr) {
|
||
|
return Evaluable<Expr, Evaluator>{expr};
|
||
|
}
|
||
|
|
||
|
/* ******** */
|
||
|
/* examples */
|
||
|
void directCall() {
|
||
|
using namespace arg;
|
||
|
|
||
|
int volatile v0 = 3, v1 = 5, v2 = 7, v3 = 9;
|
||
|
int result;
|
||
|
|
||
|
auto expr = _1 * -(_2 + _0) + _3 * _0;
|
||
|
auto fElem = bind<EvaluatorElem>(expr);
|
||
|
|
||
|
result = fElem(v0, v1, v2, v3);
|
||
|
|
||
|
std::printf("result: %d\n", result);
|
||
|
}
|
||
|
|
||
|
void lambda() {
|
||
|
using namespace arg;
|
||
|
|
||
|
std::vector<int> v{5, 7, 3, 1, 6, 9, 4, 2};
|
||
|
std::vector<int> w(v.size());
|
||
|
|
||
|
std::sort(std::begin(v), std::end(v), bind<EvaluatorElem>(_1 < _0));
|
||
|
std::transform(std::begin(v), std::begin(w), std::begin(w), bind<EvaluatorElem>(_0 * _0));
|
||
|
|
||
|
for(auto i: v) std::printf("%d ", i);
|
||
|
std::puts("");
|
||
|
|
||
|
for(auto i: w) std::printf("%d ", i);
|
||
|
std::puts("");
|
||
|
}
|
||
|
|
||
|
int main() {
|
||
|
directCall();
|
||
|
lambda();
|
||
|
}
|