├── README.md ├── lazyexpr.h ├── lazyops.h └── lazyval.h /README.md: -------------------------------------------------------------------------------- 1 | # LazyVal 2 | Header only expression template interface for vector types in C++. 3 | ## What can this do ? 4 | 1) Do straightforward arithmetic operations with vector types (class only needs constructor(size), size() and operator[] methods) using operator overloading (+,-,*,/) 5 | 2) Lazy evaluate only the needed results (no internal caching). Other than that, you can chain expresssions to your liking, without any additional runtime cost ! 6 | Here is example of what you can do: 7 | ```cpp 8 | #include "lazyval.h" 9 | using namespace Lazy; 10 | auto a = std::vector(48, 6); 11 | // Create templated symbolic expression tree 12 | auto t = 4 * (a + a + a + a + 4) / std::vector(48, 6); 13 | // Evaluate only t[4] 14 | std::cout << t[4]; 15 | // Evaluate the whole vector, use this if you need to cache/get all the results. 16 | std::vector v = t; 17 | ``` 18 | ## Basics 19 | The size is always equal to the smallest vector in the whole expression tree. Scalars apply to all elements, but don't affect the size. 20 | For forced evaluation or conversion, please use either conversion operator or eval() member method. 21 | If you want to apply a lambda over 1 - 3 operands, use map() functions. 22 | Ternary operator is possible using cond() function. 23 | ## Requirements 24 | Works with C++17 and GCC, Clang. (Maybe MSVC too...) 25 | -------------------------------------------------------------------------------- /lazyexpr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "lazyops.h" 7 | namespace Lazy { 8 | 9 | template 10 | struct NaryExpr { 11 | 12 | template 13 | using NotInit = std::negation>>; 14 | template 15 | using IfScalar = std::is_scalar::type>; 16 | 17 | std::tuple args; 18 | const Op op; // = Op(); 19 | // Uncomment if you want... There is really no need 20 | //NaryExpr(Ts && ... _args) : args(std::forward(_args)...) {} 21 | NaryExpr(Op&& op, Ts && ... _args) : args(std::forward(_args)...), op(std::forward(op)) {} 22 | 23 | inline size_t size() const { 24 | const auto call = [](Ts const& ... arg) { return std::min({ size_wrapper(arg)... }); }; 25 | return std::apply(call, args); 26 | } 27 | 28 | template ::value>> 29 | inline operator Tres() const { return eval(); } 30 | 31 | template 32 | inline Tres eval() const { 33 | size_t res_size = size(); 34 | res_size = res_size == size_t(-1) ? 1 : res_size; 35 | auto res = Tres(res_size); 36 | for(size_t i = 0; i < res_size; i++) 37 | res[i] = operator[](i); 38 | return res; 39 | } 40 | 41 | inline auto operator[](size_t i)const { 42 | const auto call = [op = this->op, i](Ts const& ... arg) { return op(index_wrapper(arg, i)...); }; 43 | return std::apply(call, args); 44 | } 45 | 46 | template 47 | static auto index_wrapper(const T& val, size_t i) { 48 | if constexpr(IfScalar::value) 49 | return val; 50 | else return val[i]; 51 | } 52 | 53 | template 54 | static size_t size_wrapper(const T& val) { 55 | if constexpr(IfScalar::value) 56 | return size_t(-1); 57 | else return val.size(); 58 | } 59 | 60 | }; 61 | 62 | template 63 | using UnExpr = NaryExpr; 64 | template 65 | using BinExpr = NaryExpr; 66 | template 67 | using TerExpr = NaryExpr; 68 | } -------------------------------------------------------------------------------- /lazyops.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | namespace Lazy{ 4 | // Ternary ops 5 | struct OpCond { 6 | template 7 | inline auto operator()(const T1& e1, const T2& e2, const T3& e3) const { 8 | return e1 ? e2 : e3; 9 | } 10 | }; 11 | struct OpLim { 12 | template 13 | inline auto operator()(const T1& e1, const T2& e2, const T3& e3) const { 14 | return std::min(std::max(e1, T1(e2)), T1(e3)); 15 | } 16 | }; 17 | // Binary ops 18 | struct OpMul { 19 | template 20 | inline auto operator()(const T1& e1, const T2& e2) const { 21 | return e1 * e2; 22 | } 23 | }; 24 | struct OpSub { 25 | template 26 | inline auto operator()(const T1& e1, const T2& e2) const { 27 | return e1 - e2; 28 | } 29 | }; 30 | struct OpDiv { 31 | template 32 | inline auto operator()(const T1& e1, const T2& e2) const { 33 | return e1 / e2; 34 | } 35 | }; 36 | struct OpAdd { 37 | template 38 | inline auto operator()(const T1& e1, const T2& e2) const { 39 | return e1 + e2; 40 | } 41 | }; 42 | struct OpEq { 43 | template 44 | inline auto operator()(const T1& e1, const T2& e2) const { 45 | return e1 == e2; 46 | } 47 | }; 48 | struct OpNeq { 49 | template 50 | inline auto operator()(const T1& e1, const T2& e2) const { 51 | return e1 != e2; 52 | } 53 | }; 54 | struct OpGt { 55 | template 56 | inline auto operator()(const T1& e1, const T2& e2) const { 57 | return e1 > e2; 58 | } 59 | }; 60 | struct OpLt { 61 | template 62 | inline auto operator()(const T1& e1, const T2& e2) const { 63 | return e1 < e2; 64 | } 65 | }; 66 | struct OpGte { 67 | template 68 | inline auto operator()(const T1& e1, const T2& e2) const { 69 | return e1 >= e2; 70 | } 71 | }; 72 | struct OpLte { 73 | template 74 | inline auto operator()(const T1& e1, const T2& e2) const { 75 | return e1 <= e2; 76 | } 77 | }; 78 | struct OpMin { 79 | template 80 | inline auto operator()(const T1& e1, const T2& e2) const { 81 | return std::min(e1, T1(e2)); 82 | } 83 | }; 84 | struct OpMax { 85 | template 86 | inline auto operator()(const T1& e1, const T2& e2) const { 87 | return std::max(e1, T1(e2)); 88 | } 89 | }; 90 | struct OpPow { 91 | template 92 | inline auto operator()(const T1& e1, const T2& e2) const { 93 | return std::pow(e1, e2); 94 | } 95 | }; 96 | 97 | 98 | // Unary ops 99 | struct OpNeg { 100 | template 101 | inline auto operator()(const T1& e1) const { 102 | return -e1; 103 | } 104 | }; 105 | struct OpSqrt { 106 | template 107 | inline auto operator()(const T1& e1) const { 108 | return std::sqrt(e1); 109 | } 110 | }; 111 | struct OpExp { 112 | template 113 | inline auto operator()(const T1& e1) const { 114 | return std::exp(e1); 115 | } 116 | }; 117 | struct OpLog2 { 118 | template 119 | inline auto operator()(const T1& e1) const { 120 | return std::log2(e1); 121 | } 122 | }; 123 | struct OpLog { 124 | template 125 | inline auto operator()(const T1& e1) const { 126 | return std::log(e1); 127 | } 128 | }; 129 | struct OpLog10 { 130 | template 131 | inline auto operator()(const T1& e1) const { 132 | return std::log10(e1); 133 | } 134 | }; 135 | struct OpSqr { 136 | template 137 | inline auto operator()(const T1& e1) const { 138 | return e1 * e1; 139 | } 140 | }; 141 | struct OpId { 142 | template 143 | inline auto operator()(const T1& e1) const { 144 | return e1; 145 | } 146 | }; 147 | } -------------------------------------------------------------------------------- /lazyval.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "lazyexpr.h" 3 | namespace Lazy { 4 | //Ternary operands 5 | template 6 | inline auto cond(U1&& a, U2&& b, U3&& c) { 7 | return NaryExpr(OpCond(), std::forward(a), std::forward(b), std::forward(c)); 8 | } 9 | template 10 | inline auto limit(U1&& a, U2&& b, U3&& c) { 11 | return NaryExpr(OpLim(), std::forward(a), std::forward(b), std::forward(c)); 12 | } 13 | template 14 | inline auto map(U1&& a, U2&& b, U3&& c, TeOp&& op = TeOp()) { 15 | return NaryExpr(std::forward(op), std::forward(a), std::forward(b), std::forward(c)); 16 | } 17 | //Binary operands 18 | template 19 | inline auto map(U1&& a, U2&& b, BiOp&& op = BiOp()) { 20 | return NaryExpr(std::forward(op), std::forward(a), std::forward(b)); 21 | } 22 | template 23 | inline auto operator+(U1&& a, U2&& b) { 24 | return NaryExpr(OpAdd(), std::forward(a), std::forward(b)); 25 | } 26 | template 27 | inline auto operator-(U1&& a, U2&& b) { 28 | return NaryExpr(OpSub(), std::forward(a), std::forward(b)); 29 | } 30 | template 31 | inline auto operator*(U1&& a, U2&& b) { 32 | return NaryExpr(OpMul(), std::forward(a), std::forward(b)); 33 | } 34 | template 35 | inline auto operator/(U1&& a, U2&& b) { 36 | return NaryExpr(OpDiv(), std::forward(a), std::forward(b)); 37 | } 38 | template 39 | inline auto operator==(U1&& a, U2&& b) { 40 | return NaryExpr(OpEq(), std::forward(a), std::forward(b)); 41 | } 42 | template 43 | inline auto operator!=(U1&& a, U2&& b) { 44 | return NaryExpr(OpNeq(), std::forward(a), std::forward(b)); 45 | } 46 | template 47 | inline auto operator>=(U1&& a, U2&& b) { 48 | return NaryExpr(OpGte(), std::forward(a), std::forward(b)); 49 | } 50 | template 51 | inline auto operator<=(U1&& a, U2&& b) { 52 | return NaryExpr(OpLte(), std::forward(a), std::forward(b)); 53 | } 54 | template 55 | inline auto operator<(U1&& a, U2&& b) { 56 | return NaryExpr(OpLt(), std::forward(a), std::forward(b)); 57 | } 58 | template 59 | inline auto operator>(U1&& a, U2&& b) { 60 | return NaryExpr(OpGt(), std::forward(a), std::forward(b)); 61 | } 62 | template 63 | inline auto min(U1&& a, U2&& b) { 64 | return NaryExpr(OpMin(), std::forward(a), std::forward(b)); 65 | } 66 | template 67 | inline auto max(U1&& a, U2&& b) { 68 | return NaryExpr(OpMax(), std::forward(a), std::forward(b)); 69 | } 70 | template 71 | inline auto pow(U1&& a, U2&& b) { 72 | return NaryExpr(OpPow(), std::forward(a), std::forward(b)); 73 | } 74 | 75 | // Unary operands 76 | template 77 | inline auto map(U1&& a, UnOp&& op = UnOp()) { 78 | return NaryExpr(std::forward(op), std::forward(a)); 79 | } 80 | template 81 | inline auto operator-(U1&& a) { 82 | return NaryExpr(OpNeg(),std::forward(a)); 83 | } 84 | template 85 | inline auto identity(U1&& a) { 86 | return NaryExpr(OpId(), std::forward(a)); 87 | } 88 | template 89 | inline auto sqrt(U1&& a) { 90 | return NaryExpr(OpSqrt(), std::forward(a)); 91 | } 92 | template 93 | inline auto sqr(U1&& a) { 94 | return NaryExpr(OpSqr(), std::forward(a)); 95 | } 96 | template 97 | inline auto exp(U1&& a) { 98 | return NaryExpr(OpExp(), std::forward(a)); 99 | } 100 | template 101 | inline auto log(U1&& a) { 102 | return NaryExpr(OpLog(), std::forward(a)); 103 | } 104 | template 105 | inline auto log2(U1&& a) { 106 | return NaryExpr(OpLog2(), std::forward(a)); 107 | } 108 | template 109 | inline auto log10(U1&& a) { 110 | return NaryExpr(OpLog10(), std::forward(a)); 111 | } 112 | } --------------------------------------------------------------------------------