From 36efc9163ca447907aa7dad76d7c0c53222d39d6 Mon Sep 17 00:00:00 2001 From: Vesa Karvonen Date: Sun, 14 Apr 2019 16:25:47 +0300 Subject: [PATCH] Functional basics --- NOTES.md | 4 +- README.md | 80 +++++++ internals/testing/arithmetic_test.cpp | 35 ++- internals/testing/comparison_test.cpp | 6 +- internals/testing/compile_only_test.cpp | 2 + internals/testing/core_test.cpp | 29 +++ internals/testing/functional_test.cpp | 67 ++++++ internals/testing/logical_test.cpp | 22 +- internals/testing/type_traits_test.cpp | 8 +- internals/testing/value_test.cpp | 18 +- provides/include/lax_v1/arithmetic.hpp | 51 +++-- provides/include/lax_v1/comparison.hpp | 29 +-- provides/include/lax_v1/core.hpp | 245 +++++++++++++++++++++ provides/include/lax_v1/force.hpp | 3 - provides/include/lax_v1/functional.hpp | 56 +++++ provides/include/lax_v1/lax.hpp | 10 + provides/include/lax_v1/lazify.hpp | 4 - provides/include/lax_v1/logical.hpp | 32 ++- provides/include/lax_v1/synopsis.hpp | 273 ++++++++++++++++++++---- provides/include/lax_v1/type.hpp | 8 - provides/include/lax_v1/type_traits.hpp | 37 ++-- provides/include/lax_v1/value.hpp | 10 - 22 files changed, 841 insertions(+), 188 deletions(-) create mode 100644 internals/testing/core_test.cpp create mode 100644 internals/testing/functional_test.cpp create mode 100644 provides/include/lax_v1/core.hpp delete mode 100644 provides/include/lax_v1/force.hpp create mode 100644 provides/include/lax_v1/functional.hpp create mode 100644 provides/include/lax_v1/lax.hpp delete mode 100644 provides/include/lax_v1/lazify.hpp delete mode 100644 provides/include/lax_v1/type.hpp delete mode 100644 provides/include/lax_v1/value.hpp diff --git a/NOTES.md b/NOTES.md index e7fadcc..d9d0bc8 100644 --- a/NOTES.md +++ b/NOTES.md @@ -1,8 +1,8 @@ Apparently VC++ 2017 is not quite ready for ```c++ -template struct value_t { - using eval = value_t; +template struct value_c { + using eval = value_c; using type = decltype(v); static constexpr auto value = v; }; diff --git a/README.md b/README.md index abb0277..0d3e26a 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,83 @@ Non-strict template metaprogramming primitives for C++. See [`synopsis.hpp`](provides/include/lax_v1/synopsis.hpp) for the API. + +## [≡](#contents) [Contents](#contents) + +- [Legend](#legend) +- [Glossary](#glossary) + - [Currying](#currying) + - [Meta expression](#meta-expression) + - [Meta function](#meta-function) + - [Meta value](#meta-value) + - [Non-strict evaluation](#non-strict-evaluation) +- [Motivation](#motivation) +- [References](#references) + +## [≡](#contents) [Legend](#legend) + +- `_m` [meta expression](#meta-expression) template +- `_c` [meta value](#meta-value) or meta value template +- `_p` primitive (internal type alias or meta expression template or class) +- `_t` type alias (from C++11) +- `_v` value constexpr (from C++17) + +## [≡](#contents) [Glossary](#glossary) + +### [≡](#contents) [Currying](#currying) + +### [≡](#contents) [Meta expression](#meta-expression) + +A lax meta expression is a type that is a subtype of a single +[meta value](#meta-value) and includes no other members aside from those defined +by that meta value. + +```c++ +using a_meta_expression = add_m, auto_c<2>>; +struct also_a_meta_expression : a_meta_expression {}; +``` + +Lax meta expression templates are typically [curried](#curried) and produce +[meta functions](#meta-function) when partially applied. + +### [≡](#contents) [Meta function](#meta-function) + +A lax meta function is a [meta value](#meta-value) that has a static `arity` +constant and an inner type template `template_m` that takes at least `arity` +types as arguments and whose result is a [meta expression](#meta-expression). + +```c++ +struct a_meta_function_c { + using eval = a_meta_function_c; + static constexpr size_t arity = N; + template + struct template_m : a_meta_expression {}; +}; +``` + +Lax higher-order meta functions take meta functions as arguments and use them +via the `apply_m` [meta expression template](#meta-expression-template). Note +that `template_m` need not support [currying](#currying) like +[meta expression templates](#meta-expression-template), because `apply_m` takes +care of that. + +### [≡](#contents) [Meta value](#meta-value) + +A lax meta value is a [meta expression](#meta-expression) whose inner `eval` +type is an alias for the meta expression itself. + +```c++ +struct a_meta_value_c { + using eval = a_meta_value_c; + // and additional members depending on value +}; +``` + +Meta values also typically include additional static constants, types, or type +templates. + +### [≡](#contents) [Non-strict evaluation](#non-strict-evaluation) + +## [≡](#contents) [Motivation](#motivation) + +## [≡](#contents) [References](#references) diff --git a/internals/testing/arithmetic_test.cpp b/internals/testing/arithmetic_test.cpp index befe4fe..f726935 100644 --- a/internals/testing/arithmetic_test.cpp +++ b/internals/testing/arithmetic_test.cpp @@ -1,34 +1,29 @@ #include "lax_v1/arithmetic.hpp" -#include "lax_v1/force.hpp" #include "config.hpp" #include -static_assert( - std::is_same_v< - lax::force_t, lax::value_t>>, - lax::value_t>); +static_assert(std::is_same_v, + lax::value_c>>, + lax::value_c>); static_assert( - std::is_same_v< - lax::force_t, lax::value_t>>, - lax::value_t>); + std::is_same_v, lax::auto_c<2>>>, + lax::auto_c<0>>); -static_assert( - std::is_same_v< - lax::force_t, lax::value_t>>, - lax::value_t>); +static_assert(std::is_same_v< + lax::force_c, lax::value_c>>, + lax::auto_c<2>>); static_assert( std::is_same_v< - lax::force_t, lax::value_t>>, - lax::value_t>); + lax::force_c, lax::value_c>>, + lax::auto_c<3>>); -static_assert( - std::is_same_v< - lax::force_t, lax::value_t>>, - lax::value_t>); +static_assert(std::is_same_v< + lax::force_c, lax::value_c>>, + lax::auto_c<1>>); -static_assert(std::is_same_v>>, - lax::value_t>); +static_assert(std::is_same_v>>, + lax::auto_c<-3>>); diff --git a/internals/testing/comparison_test.cpp b/internals/testing/comparison_test.cpp index 300f174..0a24386 100644 --- a/internals/testing/comparison_test.cpp +++ b/internals/testing/comparison_test.cpp @@ -2,8 +2,6 @@ #include "config.hpp" -static_assert( - lax::value_of_v, lax::value_t>>); +static_assert(lax::gt_m, lax::auto_c<1>>::value); -static_assert( - !lax::value_of_v, lax::value_t>>); +static_assert(!lax::eq_m, lax::auto_c<1>>::value); diff --git a/internals/testing/compile_only_test.cpp b/internals/testing/compile_only_test.cpp index 76e8197..bf79638 100644 --- a/internals/testing/compile_only_test.cpp +++ b/internals/testing/compile_only_test.cpp @@ -1 +1,3 @@ +#include "lax_v1/lax.hpp" + int main() { return 0; } diff --git a/internals/testing/core_test.cpp b/internals/testing/core_test.cpp new file mode 100644 index 0000000..b00319b --- /dev/null +++ b/internals/testing/core_test.cpp @@ -0,0 +1,29 @@ +#include "lax_v1/arithmetic.hpp" +#include "lax_v1/core.hpp" +#include "lax_v1/functional.hpp" +#include "lax_v1/type_traits.hpp" + +#include "config.hpp" + +struct X; +struct Y; +struct Z; + +static_assert( + std::is_same_v< + lax::force_deep_c< + lax::seq_c, + lax::add_m, lax::auto_c<2>>>, + lax::remove_pointer_m>, + lax::seq_c, lax::auto_c<2>>>, + lax::fn_c, lax::adds_m>>>, + lax::seq_c< + lax::from_primitive_c, + lax::closure_c, + lax::add_m, lax::auto_c<2>>>, + lax::type_c, + lax::seq_c>, + lax::function_c< + X, + lax::function_c>>>>>); diff --git a/internals/testing/functional_test.cpp b/internals/testing/functional_test.cpp new file mode 100644 index 0000000..227e2bb --- /dev/null +++ b/internals/testing/functional_test.cpp @@ -0,0 +1,67 @@ +#include "lax_v1/arithmetic.hpp" +#include "lax_v1/comparison.hpp" +#include "lax_v1/functional.hpp" +#include "lax_v1/type_traits.hpp" + +#include "config.hpp" + +static_assert(lax::apply_m, lax::neg_m<>>, + lax::auto_c<2>>::value == 2); + +static_assert(lax::apply_m, + lax::neg_m<>, + lax::add_m>>, + lax::auto_c<1>>::value == 1); + +static_assert( + lax::apply_m< + lax::pipes_m>, lax::neg_m<>, lax::dec_m<>>, + lax::auto_c<1>>::value == -3); + +static_assert(lax::thru_m, + lax::add_m>, + lax::neg_m<>, + lax::add_m>, + lax::eq_m>>::value == true); + +struct V; + +static_assert(lax::apply_m>>, + lax::auto_c<1>>::value == 0); + +static_assert(lax::thru_m, + lax::remove_all_extents_m<>, + lax::remove_pointer_m<>, + lax::alignment_of_m<>>::value == alignof(short)); + +static_assert(lax::thru_m, + lax::div_m>, + lax::constant_m>>::value == 101); + +template +using adhoc_t = lax::as_type_trait_t< + lax::pipe_m, lax::remove_pointer_m<>>, + T>; + +static_assert(std::is_same_v, int>); + +template +inline constexpr auto adhoc_v = lax::as_value_trait_v< + lax::pipe_m, lax::alignment_of_m<>>, + T>; + +static_assert(adhoc_v == alignof(int *)); + +struct F; + +template +inline constexpr auto factorial_v = lax::apply_m< + lax::fix_m, + lax::if_m< + lax::eq_m, V>, + lax::auto_c<1>, + lax::mul_m>>>>>>, + lax::auto_c>::value; + +static_assert(factorial_v<5> == 120); diff --git a/internals/testing/logical_test.cpp b/internals/testing/logical_test.cpp index d5044c0..f76ecaa 100644 --- a/internals/testing/logical_test.cpp +++ b/internals/testing/logical_test.cpp @@ -2,14 +2,20 @@ #include "config.hpp" -static_assert( - lax::value_of_v< - lax::if_m, lax::value_t, lax::value_t>> == - 1); +static_assert(lax::if_m, lax::auto_c<1>, lax::auto_c<2>>::value == + 1); + +static_assert(lax::if_m, lax::auto_c<1>, lax::auto_c<2>>::value == + 2); + +static_assert(!lax::and_m, lax::auto_c<0>>>::value); static_assert( - lax::value_of_v< - lax::if_m, lax::value_t, lax::value_t>> == - 2); + lax::or_m, lax::auto_c<0>>>::value); + +static_assert(lax::not_m::value); -static_assert(lax::value_of_v>); +static_assert(lax::switch_m>, + lax::case_c>, + lax::default_c>>::value == 1); diff --git a/internals/testing/type_traits_test.cpp b/internals/testing/type_traits_test.cpp index 9499028..f6c5de4 100644 --- a/internals/testing/type_traits_test.cpp +++ b/internals/testing/type_traits_test.cpp @@ -4,23 +4,25 @@ #include "config.hpp" -static_assert(lax::value_of_v>>); +static_assert(lax::is_array_m>::value); template struct is_stored_plain_m : lax::or_m< lax::and_m< lax::is_pointer_m, - lax::lte_m, + lax::lte_m, lax::alignment_of_m>>>, lax::and_m, is_stored_plain_m>>> {}; template static inline constexpr bool is_stored_plain_v = - lax::value_of_v>>; + is_stored_plain_m>::value; static_assert(!is_stored_plain_v); static_assert(!is_stored_plain_v); static_assert(is_stored_plain_v); static_assert(is_stored_plain_v); + +static_assert(!lax::is_same_m, lax::type_c>::value); diff --git a/internals/testing/value_test.cpp b/internals/testing/value_test.cpp index 043c715..6a578f5 100644 --- a/internals/testing/value_test.cpp +++ b/internals/testing/value_test.cpp @@ -1,19 +1,19 @@ -#include "lax_v1/value.hpp" +#include "lax_v1/core.hpp" #include "config.hpp" #include -static_assert(std::is_same_v::value_type, bool>); +static_assert(std::is_same_v::value_type, bool>); -static_assert(std::is_same_v::value_type, char>); -static_assert(std::is_same_v::value_type, short>); -static_assert(std::is_same_v::value_type, int>); +static_assert(std::is_same_v::value_type, char>); +static_assert(std::is_same_v::value_type, short>); +static_assert(std::is_same_v::value_type, int>); static_assert( - std::is_same_v::value_type, unsigned>); -static_assert(std::is_same_v::value_type, long>); -static_assert(std::is_same_v::value_type, + std::is_same_v::value_type, unsigned>); +static_assert(std::is_same_v::value_type, long>); +static_assert(std::is_same_v::value_type, unsigned long>); static_assert( - std::is_same_v::value_type, + std::is_same_v::value_type, unsigned long long>); diff --git a/provides/include/lax_v1/arithmetic.hpp b/provides/include/lax_v1/arithmetic.hpp index 361068e..118fe58 100644 --- a/provides/include/lax_v1/arithmetic.hpp +++ b/provides/include/lax_v1/arithmetic.hpp @@ -1,32 +1,29 @@ #pragma once -#include "lax_v1/value.hpp" +#include "lax_v1/core.hpp" #include -template -struct lax_v1::add_m - : value_t, value_type_of_t>, - value_of_v + value_of_v> {}; - -template -struct lax_v1::div_m - : value_t, value_type_of_t>, - value_of_v / value_of_v> {}; - -template -struct lax_v1::mod_m - : value_t, value_type_of_t>, - value_of_v % value_of_v> {}; - -template -struct lax_v1::mul_m - : value_t, value_type_of_t>, - value_of_v * value_of_v> {}; - -template -struct lax_v1::sub_m - : value_t, value_type_of_t>, - value_of_v - value_of_v> {}; - -template struct lax_v1::neg_m : auto_t<-value_of_v> {}; +#define LAX(name, op) \ + namespace lax_v1 { \ + template \ + using name##_p = value_c< \ + std::common_type_t, \ + Lhs::value op Rhs::value>; \ + } \ + template \ + struct lax_v1::name##_m : primitive_c {}; + +LAX(add, +) +LAX(div, /) +LAX(mod, %) +LAX(mul, *) +LAX(sub, -) +#undef LAX + +namespace lax_v1 { +template using neg_p = auto_c<-Expr::value>; +} // namespace lax_v1 + +template +struct lax_v1::neg_m : primitive_c {}; diff --git a/provides/include/lax_v1/comparison.hpp b/provides/include/lax_v1/comparison.hpp index dfd1980..8548d30 100644 --- a/provides/include/lax_v1/comparison.hpp +++ b/provides/include/lax_v1/comparison.hpp @@ -1,18 +1,19 @@ #pragma once -#include "lax_v1/value.hpp" +#include "lax_v1/core.hpp" -template -struct lax_v1::eq_m : auto_t<(value_of_v == value_of_v)> {}; +#define LAX(name, op) \ + namespace lax_v1 { \ + template \ + using name##_p = value_c; \ + } \ + template \ + struct lax_v1::name##_m : primitive_c {}; -template -struct lax_v1::gt_m : auto_t<(value_of_v> value_of_v)> {}; - -template -struct lax_v1::gte_m : auto_t<(value_of_v >= value_of_v)> {}; - -template -struct lax_v1::lt_m : auto_t<(value_of_v < value_of_v)> {}; - -template -struct lax_v1::lte_m : auto_t<(value_of_v <= value_of_v)> {}; +LAX(eq, ==) +LAX(gt, >) +LAX(gte, >=) +LAX(lt, <) +LAX(lte, <=) +LAX(neq, !=) +#undef LAX diff --git a/provides/include/lax_v1/core.hpp b/provides/include/lax_v1/core.hpp new file mode 100644 index 0000000..3426297 --- /dev/null +++ b/provides/include/lax_v1/core.hpp @@ -0,0 +1,245 @@ +#pragma once + +#include "lax_v1/synopsis.hpp" + +#include + +// + +namespace lax_v1 { +class apply_private { + template friend struct apply_m; + template struct t { using eval = t; }; + template struct concat; + template struct take_n; + template struct drop_n; + template struct apply_exact; + template struct apply_rest; + template struct apply; +}; + +template struct closure_c { + using eval = closure_c; + static constexpr size_t arity = Function::arity - sizeof...(BoundActuals); + template + struct template_m + : force_c> {}; +}; +} // namespace lax_v1 + +// + +template struct lax_v1::force_deep_from_whnf_m : WHNF {}; + +template +struct lax_v1::force_deep_from_whnf_m> : type_c {}; + +template +struct lax_v1::force_deep_from_whnf_m> + : function_c {}; + +template +struct lax_v1::force_deep_from_whnf_m< + lax_v1::closure_c> + : closure_c {}; + +template