diff --git a/lib/dynamic_type/src/dynamic_type/dynamic_type.h b/lib/dynamic_type/src/dynamic_type/dynamic_type.h index 23ac02adde8..bf0d08de5af 100644 --- a/lib/dynamic_type/src/dynamic_type/dynamic_type.h +++ b/lib/dynamic_type/src/dynamic_type/dynamic_type.h @@ -144,12 +144,10 @@ struct DynamicType { Containers::template is_candidate_type; template - static constexpr bool can_cast_to = any_check( - [](auto t) { - using From = typename decltype(t)::type; - return requires(From from) { - (T)(from); - }; + static constexpr bool can_cast_to = std::apply( + [](auto... ts) constexpr { + return ( + (requires(typename decltype(ts)::type from) { (T)(from); }) || ...); }, type_identities_as_tuple); @@ -425,14 +423,15 @@ struct DynamicType { } template - static constexpr bool has_square_bracket = any_check( - [](auto t) { - using T = typename decltype(t)::type; - return requires(T & tt, IndexT & idx) { - { - tt[idx] - } -> std::same_as; - }; + static constexpr bool has_square_bracket = std::apply( + [](auto... ts) constexpr { + return ( + (requires(typename decltype(ts)::type & tt, IndexT & idx) { + { + tt[idx] + } -> std::same_as; + }) || + ...); }, type_identities_as_tuple); @@ -469,10 +468,9 @@ struct DynamicType { DEFINE_SQUARE_BRACKET_OPERATOR(const) #undef DEFINE_SQUARE_BRACKET_OPERATOR - static constexpr bool has_any_square_bracket = any_check( - [](auto t) { - using IndexT = typename decltype(t)::type; - return has_square_bracket; + static constexpr bool has_any_square_bracket = std::apply( + [](auto... ts) constexpr { + return (has_square_bracket || ...); }, type_identities_as_tuple); @@ -606,14 +604,15 @@ struct DynamicType { // arguments. I believe it is doable, but I decide to leave it for future. }; -template -struct is_dynamic_type : std::false_type {}; +template +inline constexpr bool is_dynamic_type_v = false; -template -struct is_dynamic_type> : std::true_type {}; +template +inline constexpr bool is_dynamic_type_v> = true; +// A DynamicType, allowing cv/ref qualifiers on the deduced type. template -constexpr bool is_dynamic_type_v = is_dynamic_type::value; +concept DynamicTypeLike = is_dynamic_type_v>; #define DEFINE_BINARY_OP(opname, op, func_name, return_type, check_existence) \ template \ @@ -744,29 +743,23 @@ DEFINE_BINARY_OP(ge, >=, operator>=, bool, false); #undef DEFINE_BINARY_OP -#define DEFINE_UNARY_OP(opname, op) \ - /*TODO: we should inline the definition of opname##_helper into requires,*/ \ - /*but I can only do this in C++20 */ \ - constexpr auto opname##_helper = [](auto x) constexpr { \ - using T = typename decltype(x)::type; \ - return requires(T t) { \ - op t; \ - }; \ - }; \ - template \ - requires(is_dynamic_type_v>&& any_check( \ - opname##_helper, \ - std::decay_t< \ - DT>::type_identities_as_tuple)) inline constexpr decltype(auto) \ - operator op(DT&& x) { \ - return std::decay_t
::dispatch( \ - [](auto&& x) -> decltype(auto) { \ - using X = std::decay_t; \ - if constexpr (requires(X && xx) { op xx; }) { \ - return op std::forward(x); \ - } \ - }, \ - std::forward
(x)); \ +#define DEFINE_UNARY_OP(opname, op) \ + template \ + requires(std::apply( \ + [](auto... ts) constexpr { \ + return ((requires(typename decltype(ts)::type t) { op t; }) || ...); \ + }, \ + std::remove_cvref_t< \ + DT>::type_identities_as_tuple)) inline constexpr decltype(auto) \ + operator op(DT&& x) { \ + return std::remove_cvref_t
::dispatch( \ + [](auto&& x) -> decltype(auto) { \ + using X = std::decay_t; \ + if constexpr (requires(X && xx) { op xx; }) { \ + return op std::forward(x); \ + } \ + }, \ + std::forward
(x)); \ } DEFINE_UNARY_OP(pos, +); @@ -782,19 +775,17 @@ DEFINE_UNARY_OP(lnot, !); // an alternative. Also, if we overloaded the operator&, how can we get the // address of the dynamic type itself? -template -auto star_defined_checker = [](auto t) { - using T = typename decltype(t)::type; - return requires(T & tt) { - { - *tt - } -> std::same_as; - }; -}; - -template -requires(is_dynamic_type_v
&& any_check( - star_defined_checker
, +template +requires(std::apply( + [](auto... ts) constexpr { + return ( + (requires(typename decltype(ts)::type & tt) { + { + *tt + } -> std::same_as; + }) || + ...); + }, DT::type_identities_as_tuple)) DT& operator*(const DT& x) { std::optional> ret = std::nullopt; @@ -815,19 +806,17 @@ operator*(const DT& x) { } // Printing -// TODO: we should inline the definition of can_print into requires, but I can -// only do this in C++20 -constexpr auto can_print = [](auto x) constexpr { - using T = typename decltype(x)::type; - return requires(std::ostream & os, T && t) { - { - os << t - } -> std::same_as; - }; -}; -template -requires(is_dynamic_type_v
&& any_check( - can_print, +template +requires(std::apply( + [](auto... ts) constexpr { + return ( + (requires(std::ostream & os, typename decltype(ts)::type && t) { + { + os << t + } -> std::same_as; + }) || + ...); + }, DT::type_identities_as_tuple)) std::ostream& operator<<(std::ostream& os, const DT& dt) { bool printed = false; @@ -849,42 +838,41 @@ operator<<(std::ostream& os, const DT& dt) { return os; } -#define DEFINE_LEFT_PPMM(opname, op) \ - /*TODO: we should inline the definition of opname##_helper into requires,*/ \ - /*but I can only do this in C++20 */ \ - constexpr auto opname##_helper = [](auto x) constexpr { \ - using X = typename decltype(x)::type; \ - return requires(X & xx) { \ - { \ - op xx \ - } -> std::same_as; \ - }; \ - }; \ - template \ - requires(is_dynamic_type_v
&& any_check( \ - opname##_helper, DT::type_identities_as_tuple)) inline constexpr DT& \ - operator op(DT & x) { \ - bool computed = false; \ - DT::for_all_types([&computed, &x](auto _) { \ - using Type = typename decltype(_)::type; \ - if constexpr (requires(Type & t) { \ - { \ - op t \ - } -> std::same_as; \ - }) { \ - if (x.template is()) { \ - op x.template as(); \ - computed = true; \ - } \ - } \ - }); \ - DYNAMIC_TYPE_CHECK( \ - computed, \ - "Cannot compute ", \ - #op, \ - x.type().name(), \ - " : incompatible type"); \ - return x; \ +#define DEFINE_LEFT_PPMM(opname, op) \ + template \ + requires(std::apply( \ + [](auto... ts) constexpr { \ + return ( \ + (requires(typename decltype(ts)::type & xx) { \ + { \ + op xx \ + } -> std::same_as; \ + }) || \ + ...); \ + }, \ + DT::type_identities_as_tuple)) inline constexpr DT& \ + operator op(DT & x) { \ + bool computed = false; \ + DT::for_all_types([&computed, &x](auto _) { \ + using Type = typename decltype(_)::type; \ + if constexpr (requires(Type & t) { \ + { \ + op t \ + } -> std::same_as; \ + }) { \ + if (x.template is()) { \ + op x.template as(); \ + computed = true; \ + } \ + } \ + }); \ + DYNAMIC_TYPE_CHECK( \ + computed, \ + "Cannot compute ", \ + #op, \ + x.type().name(), \ + " : incompatible type"); \ + return x; \ } DEFINE_LEFT_PPMM(lpp, ++); @@ -893,20 +881,21 @@ DEFINE_LEFT_PPMM(lmm, --); #undef DEFINE_LEFT_PPMM #define DEFINE_RIGHT_PPMM(opname, op) \ - /*TODO: we should inline the definition of opname##_helper into requires,*/ \ - /*but I can only do this in C++20 */ \ - template \ - constexpr auto opname##_helper = [](auto x) constexpr { \ - using X = typename decltype(x)::type; \ - if constexpr (requires(X & xx) { xx op; }) { \ - using ResultType = decltype(std::declval() op); \ - return std::is_constructible_v; \ - } \ - return false; \ - }; \ - template \ - requires(is_dynamic_type_v
&& any_check( \ - opname##_helper, \ + template \ + requires(std::apply( \ + [](auto... ts) constexpr { \ + return ( \ + ([] { \ + using X = typename decltype(ts)::type; \ + if constexpr (requires(X & xx) { xx op; }) { \ + using ResultType = decltype(std::declval() op); \ + return std:: \ + is_constructible_v; \ + } \ + return false; \ + }()) || \ + ...); \ + }, \ DT::type_identities_as_tuple)) inline constexpr DT \ operator op(DT& x, int) { \ DT ret; \ @@ -937,12 +926,12 @@ DEFINE_RIGHT_PPMM(rmm, --); #undef DEFINE_RIGHT_PPMM -#define DEFINE_ASSIGNMENT_OP(op, assign_op) \ - template \ - requires(is_dynamic_type_v
&& (requires(DT dt, T t) { \ - dt op t; \ - })) inline constexpr DT& operator assign_op(DT & x, const T & y) { \ - return x = x op y; \ +#define DEFINE_ASSIGNMENT_OP(op, assign_op) \ + template \ + requires(requires(DT dt, T t) { \ + dt op t; \ + }) inline constexpr DT& operator assign_op(DT & x, const T & y) { \ + return x = x op y; \ } DEFINE_ASSIGNMENT_OP(+, +=); @@ -962,7 +951,7 @@ DEFINE_ASSIGNMENT_OP(>>, >>=); // Check that, whether there exist two different types T and U, where both T and // U are contained in the type list of dynamic type DT, and T == U is defined. -template +template constexpr bool has_cross_type_equality = any(remove_void_from_tuple(DT::for_all_types([](auto t) { using T = typename decltype(t)::type; diff --git a/lib/dynamic_type/src/dynamic_type/type_traits.h b/lib/dynamic_type/src/dynamic_type/type_traits.h index b195f2d028f..67dd6607ed0 100644 --- a/lib/dynamic_type/src/dynamic_type/type_traits.h +++ b/lib/dynamic_type/src/dynamic_type/type_traits.h @@ -251,102 +251,6 @@ static_assert(!belongs_to); namespace dynamic_type { -// Take the cartesion product of two tuples. -// For example: -// cartesian_product((1, 2), (3, 4)) = ((1, 3), (1, 4), (2, 3), (2, 4)) -template -constexpr auto cartesian_product(Tuple t) { - return std::apply( - [](auto... ts) constexpr { - return std::make_tuple(std::make_tuple(ts)...); - }, - t); -} - -template -constexpr auto cartesian_product(Tuple1 first, OtherTuples... others) { - auto c_first = cartesian_product(first); - auto c_others = cartesian_product(others...); - // cat one item in c_first with all the items in c_others - auto cat_one_first_all_others = [c_others](auto first_item) { - return std::apply( - [first_item](auto... other_item) constexpr { - return std::make_tuple(std::tuple_cat(first_item, other_item)...); - }, - c_others); - }; - return std::apply( - [cat_one_first_all_others](auto... first_items) constexpr { - return std::tuple_cat(cat_one_first_all_others(first_items)...); - }, - c_first); -} - -// For example: - -static_assert( - cartesian_product(std::make_tuple(1.0, true)) == - std::make_tuple(std::make_tuple(1.0), std::make_tuple(true))); - -static_assert( - cartesian_product(std::make_tuple(1.0, true), std::make_tuple(2.0f, 4)) == - std::make_tuple( - std::make_tuple(1.0, 2.0f), - std::make_tuple(1.0, 4), - std::make_tuple(true, 2.0f), - std::make_tuple(true, 4))); - -static_assert( - cartesian_product( - std::make_tuple(1.0, true), - std::make_tuple(2.0f, 4), - std::make_tuple(std::size_t(0), nullptr)) == - std::make_tuple( - std::make_tuple(1.0, 2.0f, std::size_t(0)), - std::make_tuple(1.0, 2.0f, nullptr), - std::make_tuple(1.0, 4, std::size_t(0)), - std::make_tuple(1.0, 4, nullptr), - std::make_tuple(true, 2.0f, std::size_t(0)), - std::make_tuple(true, 2.0f, nullptr), - std::make_tuple(true, 4, std::size_t(0)), - std::make_tuple(true, 4, nullptr))); - -} // namespace dynamic_type - -namespace dynamic_type { - -// Can I find an x from tuple1 and a y from tuple12 such that f(x, y) is -// true? f(x, y) must be defined for all x in tuple1 and y in tuple2. -template -constexpr bool any_check(Fun f, Tuples... tuples) { - auto c = cartesian_product(tuples...); - return std::apply( - [f](auto... candidates) constexpr { - return any(std::apply(f, candidates)...); - }, - c); -} - -// For example: -static_assert( - any_check([](auto x) constexpr { return x > 0; }, std::make_tuple(1, -1))); -static_assert(!any_check( - [](auto x) constexpr { return x > 0; }, - std::make_tuple(-2, -1))); - -static_assert(any_check( - [](auto x, auto y) constexpr { return (x + y) > 0; }, - std::make_tuple(2.0, 1), - std::make_tuple(-2, -1))); -static_assert(!any_check( - [](auto x, auto y) constexpr { return (x + y) > 0; }, - std::make_tuple(1.0, 1), - std::make_tuple(-2, -1))); - -} // namespace dynamic_type - -namespace dynamic_type { - // Check if all the types in the tuple are the same. If the tuple is empty, or // the provided type is not a tuple, then it is considered to be false.