pfor/src/pfor/expression/expression.h

242 lines
7.9 KiB
C++

#ifndef PFOR_PFOR_EXPRESSION_EXPRESSION_H
#define PFOR_PFOR_EXPRESSION_EXPRESSION_H
#include <tuple>
#include "op.h"
#include "tag.h"
#include "../index/index.h"
#include "../index/traits.h"
#include "../tuple_view.h"
namespace pfor {
namespace expr {
struct IgnoredId;
template<typename...> struct Expression;
template<typename...> struct Constant;
template<typename T>
struct Constant<T> {
using IsExpression = tag::Expression;
using DataType = T;
using Id = IgnoredId;
using ThisType = Constant<DataType, Id>;
DataType data;
Constant() = delete;
Constant(DataType const& data): data{data} {}
Constant(ThisType const& e): data{e.data} {}
inline DataType const& eval() { return data; }
inline DataType const& operator[](index::Value) const { return data; }
template<typename Index, std::enable_if_t<index::isIndex<Index>>* = nullptr>
inline ThisType& operator[](Index const&) { return *this; }
};
template<typename T>
struct Constant<T const*> {
using IsExpression = tag::Expression;
using DataType = T const*;
using Id = IgnoredId;
using ThisType = Constant<DataType, Id>;
DataType data;
Constant() = delete;
Constant(DataType const& data): data{data} {}
Constant(ThisType const& e): data{e.data} {}
inline T const& operator[](index::Value i) const { return data[i]; }
template<typename Index, std::enable_if_t<index::isIndex<Index>>* = nullptr>
inline ThisType& operator[](Index const&) { return *this; }
};
template<typename Expr, typename Prop>
struct Constant<index::IndexImpl<Expr, Prop>> {
using IsExpression = tag::Expression;
using DataType = index::Value;
using Id = IgnoredId;
using ThisType = Constant<DataType, Id>;
using Index = index::IndexImpl<Expr, Prop>;
Constant() = delete;
Constant(Index) {}
Constant(ThisType const&) {}
inline DataType operator[](index::Value i) const { return Index::eval(i); }
};
template<typename T, typename Id, typename Index_>
struct Expression<T*, Id, Index_> {
using IsExpression = tag::Expression;
using DataType = T*;
using Index = Index_;
using ThisType = Expression<DataType, Id, Index>;
DataType data;
Expression() = delete;
Expression(DataType data): data{data} {}
inline auto& operator[](index::Value i) { return data[Index::eval(i)]; }
template<typename NIndex, std::enable_if_t<index::isIndex<NIndex>>* = nullptr>
inline auto operator[](NIndex const&) { return Expression<DataType, Id, NIndex>{data}; }
inline auto begin() { return std::begin(data); }
inline auto end() { return std::end(data); }
// inline auto operator=(ThisType const& rhs) {
// return Expression<op::Assign, ThisType, ThisType>{{}, *this, rhs};
// }
template<typename Rhs, std::enable_if_t<isExpression<Rhs>>* = nullptr>
inline auto operator=(Rhs rhs) {
return Expression<op::Assign, ThisType, Rhs>{{}, *this, rhs};
}
template<typename Rhs, std::enable_if_t<not isExpression<Rhs>>* = nullptr>
inline auto operator=(Rhs rhs) {
return Expression<op::Assign, ThisType, Constant<Rhs>>{{}, *this, rhs};
}
inline auto operator++(int) { return Expression<op::PostIncr, ThisType>{{}, *this}; }
inline auto operator--(int) { return Expression<op::PostDecr, ThisType>{{}, *this}; }
inline auto operator++() { return Expression<op::PreIncr, ThisType>{{}, *this}; }
inline auto operator--() { return Expression<op::PreDecr, ThisType>{{}, *this}; }
};
template<typename T, std::size_t n, typename Id, typename Index_>
struct Expression<std::array<T, n>, Id, Index_> {
using IsExpression = tag::Expression;
using DataTypeRaw = std::array<T, n>;
using DataType = DataTypeRaw&;
using Index = Index_;
using ThisType = Expression<DataTypeRaw, Id, Index>;
DataType data;
Expression() = delete;
Expression(DataType data): data{data} {}
inline auto& operator[](index::Value i) { return data[Index::eval(i)]; }
template<typename NIndex, std::enable_if_t<index::isIndex<NIndex>>* = nullptr>
inline auto operator[](NIndex const&) { return Expression<DataTypeRaw, Id, NIndex>{data}; }
inline auto begin() { return std::begin(data); }
inline auto end() { return std::end(data); }
// inline auto operator=(ThisType const& rhs) {
// return Expression<op::Assign, ThisType, ThisType>{{}, *this, rhs};
// }
template<typename Rhs, std::enable_if_t<isExpression<Rhs>>* = nullptr>
inline auto operator=(Rhs rhs) {
return Expression<op::Assign, ThisType, Rhs>{{}, *this, rhs};
}
template<typename Rhs, std::enable_if_t<not isExpression<Rhs>>* = nullptr>
inline auto operator=(Rhs rhs) {
return Expression<op::Assign, ThisType, Constant<Rhs>>{{}, *this, rhs};
}
inline auto operator++(int) { return Expression<op::PostIncr, ThisType>{{}, *this}; }
inline auto operator--(int) { return Expression<op::PostDecr, ThisType>{{}, *this}; }
inline auto operator++() { return Expression<op::PreIncr, ThisType>{{}, *this}; }
inline auto operator--() { return Expression<op::PreDecr, ThisType>{{}, *this}; }
};
template<typename Op, typename... ETs>
struct Expression<Op, ETs...> {
using IsExpression = tag::Expression;
using ThisType = Expression<Op, ETs...>;
static constexpr std::size_t arity = sizeof...(ETs);
using AritySequence = decltype(std::make_index_sequence<arity>());
Op op;
std::tuple<ETs...> operands;
Expression() = delete;
Expression(Op const& op, ETs const&... e): op(op), operands{e...} {}
Expression(ThisType const& e): op(e.op), operands{e.operands} {}
Expression(ThisType&& e): op(std::move(e.op)), operands{std::move(e.operands)} {}
inline decltype(auto) eval() { return eval(AritySequence{}); }
inline decltype(auto) operator[](index::Value i) { return eval(i, AritySequence{}); }
// inline auto operator=(ThisType const& rhs) {
// return Expression<op::Assign, ThisType, ThisType>{{}, *this, rhs};
// }
template<typename Rhs, std::enable_if_t<isExpression<Rhs>>* = nullptr>
inline auto operator=(Rhs rhs) {
return Expression<op::Assign, ThisType, Rhs>{{}, *this, rhs};
}
template<typename Rhs, std::enable_if_t<not isExpression<Rhs>>* = nullptr>
inline auto operator=(Rhs rhs) {
return Expression<op::Assign, ThisType, Constant<Rhs>>{{}, *this, rhs};
}
template<std::size_t... indices>
inline decltype(auto) eval(std::index_sequence<indices...>) {
return op.eval(std::get<indices>(operands)...);
}
template<std::size_t... indices>
inline decltype(auto) eval(index::Value i, std::index_sequence<indices...>) {
return op.eval(i, std::get<indices>(operands)...);
}
inline auto operator++(int) { return Expression<op::PostIncr, ThisType>{{}, *this}; }
inline auto operator--(int) { return Expression<op::PostDecr, ThisType>{{}, *this}; }
inline auto operator++() { return Expression<op::PreIncr, ThisType>{{}, *this}; }
inline auto operator--() { return Expression<op::PreDecr, ThisType>{{}, *this}; }
};
template<typename Op, typename View, typename... ETs>
struct Expression<op::View<Op>, View, ETs...> {
using IsExpression = tag::Expression;
using ThisType = Expression<Op, View, ETs...>;
using Tuple = std::tuple<ETs...>;
using RefExpression = Expression<Op, ETs...>;
static constexpr std::size_t arity = packSize<View>;
using AritySequence = decltype(std::make_index_sequence<arity>());
Op op;
TupleView<Tuple, View> operands;
Expression() = delete;
Expression(RefExpression& e): op(e.op), operands{e.operands} {}
Expression(ThisType const& e): op(e.op), operands{e.operands} {}
Expression(ThisType&& e): op(std::move(e.op)), operands{std::move(e.operands)} {}
inline decltype(auto) eval() { return eval(AritySequence{}); }
inline decltype(auto) operator[](index::Value i) { return eval(i, AritySequence{}); }
template<std::size_t... indices>
inline decltype(auto) eval(std::index_sequence<indices...>) {
return op.eval(std::get<indices>(operands)...);
}
template<std::size_t... indices>
inline decltype(auto) eval(index::Value i, std::index_sequence<indices...>) {
return op.eval(i, std::get<indices>(operands)...);
}
};
}
}
#endif