diff --git a/include/iris/x4/core/detail/parse_alternative.hpp b/include/iris/x4/core/detail/parse_alternative.hpp index 352330273..95d6e41f3 100644 --- a/include/iris/x4/core/detail/parse_alternative.hpp +++ b/include/iris/x4/core/detail/parse_alternative.hpp @@ -106,26 +106,6 @@ struct pass_non_variant_attribute } }; -// Unwrap single element sequences -template - requires traits::is_size_one_sequence_v -struct pass_non_variant_attribute -{ - using attr_type = typename std::remove_reference_t< - alloy::tuple_element_t<0, Attr> - >; - using pass = pass_parser_attribute; - using type = typename pass::type; - - template - [[nodiscard]] static constexpr type - call(Attr_& attr) - noexcept(noexcept(pass::call(alloy::get<0>(attr)))) - { - return pass::call(alloy::get<0>(attr)); - } -}; - template requires (!traits::is_variant_v) struct pass_parser_attribute diff --git a/include/iris/x4/core/detail/parse_into_container.hpp b/include/iris/x4/core/detail/parse_into_container.hpp index 8a40b2493..d36ecb322 100644 --- a/include/iris/x4/core/detail/parse_into_container.hpp +++ b/include/iris/x4/core/detail/parse_into_container.hpp @@ -87,11 +87,10 @@ struct parse_into_container_base_impl // ------------------------------------------------------ - // Parser has attribute && it is NOT tuple-like + // Parser has attribute template Se, class Context, X4Attribute Attr> requires - has_attribute_v && - (!alloy::is_tuple_like_v) + has_attribute_v [[nodiscard]] static constexpr bool call( Parser const& parser, It& first, Se const& last, @@ -99,23 +98,7 @@ struct parse_into_container_base_impl ) { // TODO: reduce call stack while keeping maintainability - return parse_into_container_base_impl::call_synthesize(parser, first, last, ctx, attr); - } - - // Parser has attribute && it is tuple-like - template Se, class Context, X4Attribute Attr> - requires - has_attribute_v && - alloy::is_tuple_like_v - [[nodiscard]] static constexpr bool - call( - Parser const& parser, It& first, Se const& last, - Context const& ctx, Attr& attr - ) noexcept(noexcept(parse_into_container_base_impl::call_synthesize(parser, first, last, ctx, alloy::get<0>(attr)))) - { - static_assert(traits::has_size_v, "Expecting a single element tuple-like"); - // TODO: reduce call stack while keeping maintainability - return parse_into_container_base_impl::call_synthesize(parser, first, last, ctx, alloy::get<0>(attr)); + return parse_into_container_base_impl::call_synthesize(parser, first, last, ctx, x4::unwrap_single_element_tuple_like(attr)); } // Parser has no attribute (pass unused) @@ -149,7 +132,7 @@ struct parse_into_container_impl It, Se, Context, typename parser_traits::attribute_type >::actual_type, - traits::container_value_t + traits::container_value_t> >> >; @@ -205,22 +188,25 @@ template< parse_into_container( Parser const& parser, It& first, Se const& last, Context const& ctx, Attr& attr -) noexcept(noexcept(parse_into_container_impl::call(parser, first, last, ctx, attr))) +) // TODO: add noexcept { static_assert( !std::same_as, "`unused_type` should not be passed to `parse_into_container`. Use `x4::assume_container(attr)`" ); - if constexpr (traits::is_variant_v) { + if constexpr (traits::is_variant_v>) { // e.g. `char` when the caller is `+char_` using attribute_type = parser_traits::attribute_type; // e.g. `std::string` when the attribute_type is `char` - using substitute_type = traits::variant_find_substitute_t>; + using substitute_type = traits::variant_find_substitute_t< + traits::unwrap_single_element_tuple_like_t, + traits::build_container_t + >; // instead of creating a temporary `substitute_type`, append directly into the emplaced alternative - auto& variant_alt = attr.template emplace(); + auto& variant_alt = x4::unwrap_single_element_tuple_like(attr).template emplace(); return parse_into_container_impl::call(parser, first, last, ctx, variant_alt); } else { return parse_into_container_impl::call(parser, first, last, ctx, attr); diff --git a/include/iris/x4/core/detail/parse_sequence.hpp b/include/iris/x4/core/detail/parse_sequence.hpp index eee6e0dcf..927e12ff1 100644 --- a/include/iris/x4/core/detail/parse_sequence.hpp +++ b/include/iris/x4/core/detail/parse_sequence.hpp @@ -49,19 +49,6 @@ struct pass_sequence_attribute_unused } }; -template -struct pass_sequence_attribute_size_one_view -{ - using type = alloy::tuple_element_t<0, Attr>; - - [[nodiscard]] static constexpr type - call(Attr& attribute) - noexcept(noexcept(alloy::get<0>(attribute))) - { - return alloy::get<0>(attribute); - } -}; - template struct pass_through_sequence_attribute { @@ -76,11 +63,8 @@ struct pass_through_sequence_attribute }; template -struct pass_sequence_attribute : std::conditional_t< - traits::is_size_one_view_v, - pass_sequence_attribute_size_one_view, - pass_through_sequence_attribute -> +struct pass_sequence_attribute + : pass_through_sequence_attribute {}; template @@ -126,21 +110,21 @@ struct partition_attribute using view = alloy::tuple_ref_t; using splitted = alloy::tuple_split_t; - using l_part = alloy::tuple_element_t<0, splitted>; - using r_part = alloy::tuple_element_t<1, splitted>; + using l_part = traits::unwrap_single_element_tuple_like_t>; + using r_part = traits::unwrap_single_element_tuple_like_t>; using l_pass = pass_sequence_attribute; using r_pass = pass_sequence_attribute; [[nodiscard]] static constexpr l_part left(Attr& s) // TODO: noexcept { - return alloy::get<0>(alloy::tuple_split(alloy::tuple_ref(s))); + return x4::unwrap_single_element_tuple_like(alloy::get<0>(alloy::tuple_split(alloy::tuple_ref(s)))); } [[nodiscard]] static constexpr r_part right(Attr& s) // TODO: noexcept { - return alloy::get<1>(alloy::tuple_split(alloy::tuple_ref(s))); + return x4::unwrap_single_element_tuple_like(alloy::get<1>(alloy::tuple_split(alloy::tuple_ref(s)))); } }; @@ -284,7 +268,7 @@ struct parse_into_container_impl> template static constexpr bool is_container_substitute = traits::is_substitute_v< typename sequence::attribute_type, - traits::container_value_t + traits::container_value_t> >; template Se, class Context, X4Attribute Attr> diff --git a/include/iris/x4/core/move_to.hpp b/include/iris/x4/core/move_to.hpp index 61c81534c..ce8908640 100644 --- a/include/iris/x4/core/move_to.hpp +++ b/include/iris/x4/core/move_to.hpp @@ -116,18 +116,7 @@ constexpr void move_to(It const&, Se const&, unused_type const&&) = delete; // t // Category specific -------------------------------------- template Dest> - requires traits::is_size_one_sequence_v -constexpr void -move_to(Source&& src, Dest& dest) - noexcept(noexcept(dest = std::forward_like(alloy::get<0>(std::forward(src))))) -{ - static_assert(!std::same_as, Dest>, "[BUG] This call should instead resolve to the overload handling identical types"); - // TODO: preliminarily invoke static_assert to check if the assignment is valid - dest = std::forward_like(alloy::get<0>(std::forward(src))); -} - -template Dest> - requires (!traits::is_size_one_sequence_v) + requires (!traits::is_single_element_tuple_like_v) constexpr void move_to(Source&& src, Dest& dest) noexcept(std::is_nothrow_assignable_v) @@ -139,8 +128,8 @@ move_to(Source&& src, Dest& dest) template Dest> requires - traits::is_same_size_sequence_v && - (!traits::is_size_one_sequence_v) + traits::is_same_size_tuple_like_v> && + (!traits::is_single_element_tuple_like_v) constexpr void move_to(Source&& src, Dest& dest) noexcept(noexcept(alloy::tuple_assign(std::forward(src), dest))) @@ -152,43 +141,40 @@ move_to(Source&& src, Dest& dest) alloy::tuple_assign(std::forward(src), dest); } +// Source is single element tuple-like and Dest is variant which *can* holds Source directly template Dest> - requires traits::is_size_one_sequence_v && traits::variant_has_substitute_v + requires + (!traits::is_single_element_tuple_like_v) && + traits::is_single_element_tuple_like_v> && + traits::variant_has_substitute_v> constexpr void move_to(Source&& src, Dest& dest) noexcept(std::is_nothrow_assignable_v) { static_assert(!std::same_as, Dest>, "[BUG] This call should instead resolve to the overload handling identical types"); - - // dest is a variant, src is a single element tuple-like that the variant - // *can* directly hold. static_assert(std::is_assignable_v); dest = std::forward(src); } +// Source is single element tuple-like and Dest is variant which *can not* holds Source directly template Dest> - requires traits::is_size_one_sequence_v && (!traits::variant_has_substitute_v) + requires + (!traits::is_single_element_tuple_like_v) && + traits::is_single_element_tuple_like_v> && + (!traits::variant_has_substitute_v>) constexpr void -move_to(Source&& src, Dest& dest) - noexcept(noexcept(dest = std::forward_like(alloy::get<0>(std::forward(src))))) +move_to(Source&& src, Dest& dest) // TODO: add noexcept { static_assert(!std::same_as, Dest>, "[BUG] This call should instead resolve to the overload handling identical types"); - // dest is a variant, src is a single element tuple-like that the variant - // cannot directly hold. We'll try to unwrap the single element tuple-like. - - // Make sure that the Dest variant can really hold Source - static_assert( - traits::variant_has_substitute_v>, - "Error! The destination variant (Dest) cannot hold the source type (Source)" - ); - - // TODO: preliminarily invoke static_assert to check if the assignment is valid dest = std::forward_like(alloy::get<0>(std::forward(src))); } +// Source is *NOT* single element tuple-like and Dest is variant template Dest> - requires (!traits::is_size_one_sequence_v) + requires + (!traits::is_single_element_tuple_like_v) && + (!traits::is_single_element_tuple_like_v>) constexpr void move_to(Source&& src, Dest& dest) noexcept(std::is_nothrow_assignable_v) @@ -199,6 +185,7 @@ move_to(Source&& src, Dest& dest) } template Dest> + requires (!traits::is_single_element_tuple_like_v) constexpr void move_to(Source&& src, Dest& dest) noexcept(std::is_nothrow_assignable_v) @@ -214,6 +201,7 @@ template struct container_appender; template Se, traits::CategorizedAttr Dest> + requires (!traits::is_single_element_tuple_like_v) constexpr void move_to(It first, Se last, Dest& dest) // never noexcept, requires container insertion @@ -243,21 +231,13 @@ move_to(It first, Se last, std::ranges::subrange& rng) rng = std::ranges::subrange(std::move(first), std::move(last)); } -template Se, traits::CategorizedAttr Dest> - requires traits::is_size_one_sequence_v -constexpr void -move_to(It first, Se last, Dest& dest) - noexcept(noexcept(x4::move_to(first, last, alloy::get<0>(dest)))) -{ - x4::move_to(first, last, alloy::get<0>(dest)); -} - // Move non-container `src` into container `dest`. // e.g. Source=std::string_view, Dest=std::string (used in `attr_parser`) template Dest> requires (!traits::X4Container) && - (!std::same_as, unused_container_type>) + (!std::same_as, unused_container_type>) && + (!traits::is_single_element_tuple_like_v) constexpr void move_to(Source&& src, Dest& dest) noexcept(std::is_nothrow_assignable_v) @@ -272,6 +252,7 @@ move_to(Source&& src, Dest& dest) } template Dest> + requires (!traits::is_single_element_tuple_like_v) constexpr void move_to(Source&& src, Dest& dest) // TODO: noexcept @@ -285,12 +266,21 @@ move_to(Source&& src, Dest& dest) } } -// Size-one tuple-like forwarding -template Dest> - requires traits::is_size_one_sequence_v +// single element tuple-like forwarding + +template Se, traits::NonUnusedAttr Dest> + requires traits::is_single_element_tuple_like_v constexpr void -move_to(Source&& src, Dest& dest) - noexcept(noexcept(x4::move_to(std::forward(src), alloy::get<0>(dest)))) +move_to(It first, Se last, Dest& dest) + noexcept(noexcept(x4::move_to(first, last, alloy::get<0>(dest)))) +{ + x4::move_to(first, last, alloy::get<0>(dest)); +} + +template + requires traits::is_single_element_tuple_like_v +constexpr void +move_to(Source&& src, Dest& dest) // TODO: add noexcept { static_assert(!std::same_as, Dest>, "[BUG] This call should instead resolve to the overload handling identical types"); diff --git a/include/iris/x4/operator/alternative.hpp b/include/iris/x4/operator/alternative.hpp index 84a1a323a..2e013bb05 100644 --- a/include/iris/x4/operator/alternative.hpp +++ b/include/iris/x4/operator/alternative.hpp @@ -31,7 +31,7 @@ namespace iris::x4 { template struct alternative : binary_parser> { - using attribute_type = traits::attribute_of_binary::type; + using attribute_type = traits::attribute_of_binary::type; using binary_parser::binary_parser; diff --git a/include/iris/x4/operator/sequence.hpp b/include/iris/x4/operator/sequence.hpp index b2d95c11d..34dad71dd 100644 --- a/include/iris/x4/operator/sequence.hpp +++ b/include/iris/x4/operator/sequence.hpp @@ -31,7 +31,7 @@ namespace iris::x4 { template struct sequence : binary_parser> { - using attribute_type = traits::attribute_of_binary::type; + using attribute_type = traits::attribute_of_binary::type; static constexpr std::size_t sequence_size = parser_traits::sequence_size + parser_traits::sequence_size; diff --git a/include/iris/x4/rule.hpp b/include/iris/x4/rule.hpp index 2f58af834..fb8ef1e3f 100644 --- a/include/iris/x4/rule.hpp +++ b/include/iris/x4/rule.hpp @@ -378,11 +378,6 @@ concept RuleAttrTransformable = RuleAttr >; -template -concept RuleAttrCompatible = - std::same_as, RuleAttr> || - RuleAttrTransformable; - } // detail template @@ -415,7 +410,8 @@ struct rule : parser> template Se, class Context, X4Attribute Exposed> requires (!std::same_as, unused_type>) && - detail::RuleAttrCompatible + (!std::same_as, RuleAttr>) && + detail::RuleAttrTransformable [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Exposed& exposed_attr) const // never noexcept; requires very complex implementation details @@ -435,39 +431,52 @@ struct rule : parser> using detail::parse_rule; // ADL - if constexpr (std::same_as, RuleAttr>) { - return static_cast(parse_rule(detail::rule_id{}, first, last, rule_agnostic_ctx, exposed_attr)); // NOLINT(bugprone-non-zero-enum-to-bool-conversion) - - } else { - static_assert(detail::RuleAttrTransformable); + static_assert(detail::RuleAttrConvertibleWithoutNarrowing); - // TODO: specialize `container_appender` case, do not create temporary + // TODO: specialize `container_appender` case, do not create temporary - RuleAttr rule_attr; - if (!static_cast(parse_rule(detail::rule_id{}, first, last, rule_agnostic_ctx, rule_attr))) { // NOLINT(bugprone-non-zero-enum-to-bool-conversion) - return false; - } + RuleAttr rule_attr; + if (!static_cast(parse_rule(detail::rule_id{}, first, last, rule_agnostic_ctx, rule_attr))) { // NOLINT(bugprone-non-zero-enum-to-bool-conversion) + return false; + } - if constexpr (is_ttp_specialization_of_v, container_appender>) { - traits::append( - exposed_attr.container, - std::make_move_iterator(traits::begin(rule_attr)), - std::make_move_iterator(traits::end(rule_attr)) - ); - } else { - static_assert(std::is_assignable_v); - exposed_attr = std::move(rule_attr); - } - return true; + if constexpr (is_ttp_specialization_of_v, container_appender>) { + traits::append( + exposed_attr.container, + std::make_move_iterator(traits::begin(rule_attr)), + std::make_move_iterator(traits::end(rule_attr)) + ); + } else { + static_assert(std::is_assignable_v); + exposed_attr = std::move(rule_attr); } + return true; + } + + // rule's attribute type is the same as the exposed one + template Se, class Context, X4Attribute Exposed> + requires + (!std::same_as, unused_type>) && + std::same_as, RuleAttr> + [[nodiscard]] constexpr bool + parse(It& first, Se const& last, Context const& ctx, Exposed& exposed_attr) const + // never noexcept; requires very complex implementation details + { + static_assert(has_attribute, "A rule must have an attribute. Check your rule definition."); + + // See the comment above + auto&& rule_agnostic_ctx = x4::remove_first_context(ctx); + + using detail::parse_rule; // ADL + return static_cast(parse_rule(detail::rule_id{}, first, last, rule_agnostic_ctx, exposed_attr)); // NOLINT(bugprone-non-zero-enum-to-bool-conversion) } template Se, class Context, X4Attribute Exposed> requires (!std::same_as, unused_type>) && - (!detail::RuleAttrCompatible) && - detail::RuleAttrConvertible && - (!detail::RuleAttrConvertibleWithoutNarrowing) + (!std::same_as, RuleAttr>) && + (!detail::RuleAttrTransformable) && + (detail::RuleAttrConvertible && !detail::RuleAttrConvertibleWithoutNarrowing) [[nodiscard]] constexpr bool parse(It&, Se const&, Context const&, Exposed&) const = delete; // Rule attribute needs narrowing conversion diff --git a/include/iris/x4/string/detail/string_parse.hpp b/include/iris/x4/string/detail/string_parse.hpp index 120a03ddd..64d78650c 100644 --- a/include/iris/x4/string/detail/string_parse.hpp +++ b/include/iris/x4/string/detail/string_parse.hpp @@ -30,9 +30,8 @@ string_parse( Attr& attr, CaseCompareFunc const& compare ) noexcept(std::same_as, unused_container_type>) { - using synthesized_value_type = traits::synthesized_value_t; - static_assert(std::same_as, traits::container_attr>); - using value_type = traits::container_value_t; + static_assert(std::same_as, traits::container_attr>); + using value_type = traits::container_value_t>; static_assert(!traits::CharLike || std::same_as, "Mixing incompatible char types is not allowed"); It it = first; @@ -77,9 +76,8 @@ string_parse( It& first, Se const& last, Attr& attr ) noexcept(std::same_as, unused_container_type>) { - using synthesized_value_type = traits::synthesized_value_t; - static_assert(std::same_as, traits::container_attr>); - using value_type = traits::container_value_t; + static_assert(std::same_as, traits::container_attr>); + using value_type = traits::container_value_t>; static_assert(!traits::CharLike || std::same_as, "Mixing incompatible char types is not allowed"); auto uc_it = ucstr.begin(); diff --git a/include/iris/x4/traits/attribute_category.hpp b/include/iris/x4/traits/attribute_category.hpp index 5032b1ff8..13da35c15 100644 --- a/include/iris/x4/traits/attribute_category.hpp +++ b/include/iris/x4/traits/attribute_category.hpp @@ -10,10 +10,11 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) =============================================================================*/ -#include -#include #include #include +#include +#include +#include #include @@ -88,12 +89,19 @@ concept NonUnusedAttr = !std::is_same_v>::type, unused_attr>; template - requires alloy::is_tuple_like_v + requires alloy::is_tuple_like_v && (!traits::is_single_element_tuple_like_v) struct attribute_category { using type = tuple_attr; }; +// attribute-category of single element tuple-like "fallthrough" into unwrapped attribute's one +template + requires alloy::is_tuple_like_v && traits::is_single_element_tuple_like_v +struct attribute_category + : attribute_category> +{}; + template requires is_variant_v> struct attribute_category diff --git a/include/iris/x4/traits/attribute_of_binary.hpp b/include/iris/x4/traits/attribute_of_binary.hpp index 0628e2429..6e7b4a109 100644 --- a/include/iris/x4/traits/attribute_of_binary.hpp +++ b/include/iris/x4/traits/attribute_of_binary.hpp @@ -16,81 +16,102 @@ #include #include +#include // TODO: move iris::type_list to separate header + #include namespace iris::x4::traits { namespace detail { -template -struct type_sequence -{ - using type = type_sequence; - - static constexpr std::size_t size = sizeof...(Ts); - - template - using prepend = type_sequence; +template +struct append_to_type_list {}; - template - using extend = U::template prepend; +template +using append_to_type_list_t = append_to_type_list::type; - template class U> - using transfer_to = U; +template +struct append_to_type_list> +{ + using type = type_list; }; -template -struct types_of_binary_init : type_sequence +template +struct append_to_type_list, unused_type, Us...> + : append_to_type_list, Us...> {}; -template<> -struct types_of_binary_init : type_sequence<> +template +struct append_to_type_list, U, Us...> + : append_to_type_list, Us...> {}; -template<> -struct types_of_binary_init : type_sequence<> +template +struct append_to_type_list, type_list, Vs...> + : append_to_type_list, Us...>, Vs...> {}; -template class BinaryParserTT, class ParserT> -struct get_types_of_binary - : types_of_binary_init::attribute_type> // TODO: unwrap -{}; +template class TupleTT, class T> +struct tuple_to_type_list; -template class BinaryParserTT, class Left, class Right> -struct get_types_of_binary> - : get_types_of_binary::template extend> -{}; +template class TupleTT, class T> +using tuple_to_type_list_t = tuple_to_type_list::type; -template class AttrTT, class T, std::size_t = T::size> -struct type_sequence_to_attribute +template class TupleTT, class T> +struct tuple_to_type_list { - using type = typename T::template transfer_to; + using type = T; }; -template class AttrTT, class T> -struct type_sequence_to_attribute - : T::template transfer_to -{}; +template class TupleTT, class... Ts> +struct tuple_to_type_list> +{ + using type = type_list...>; +}; + +template class TupleTT, class T> +using tuple_to_type_list_t = tuple_to_type_list::type; + +template class TupleTT, class TypeList> +struct type_list_to_tuple {}; -template class AttrTT, class T> -struct type_sequence_to_attribute +template class TupleTT> +struct type_list_to_tuple> { using type = unused_type; }; +template class TupleTT, class T> +struct type_list_to_tuple> +{ + using type = T; +}; + +template class TupleTT, class T0, class T1, class... Ts> +struct type_list_to_tuple> +{ + using type = TupleTT; +}; + +template class TupleTT, class TypeList> +using type_list_to_tuple_t = type_list_to_tuple::type; + } // detail template< - template class AttrTT, - template class BinaryParserTT, - class Left, class Right + template class TupleTT, + class LeftParser, class RightParser > struct attribute_of_binary { - using type = detail::type_sequence_to_attribute< - AttrTT, - typename detail::get_types_of_binary>::type - >::type; + using type = detail::type_list_to_tuple_t< + TupleTT, + detail::append_to_type_list_t< + type_list<>, + detail::tuple_to_type_list_t::attribute_type>, + detail::tuple_to_type_list_t::attribute_type> + > + >; }; } // iris::x4::traits diff --git a/include/iris/x4/traits/container_traits.hpp b/include/iris/x4/traits/container_traits.hpp index c5c9b28fc..157efc4a7 100644 --- a/include/iris/x4/traits/container_traits.hpp +++ b/include/iris/x4/traits/container_traits.hpp @@ -12,10 +12,9 @@ =============================================================================*/ #include -#include -#include -#include +#include +#include #include #include @@ -471,9 +470,6 @@ struct build_container template using build_container_t = typename build_container::type; -template -struct build_container> : build_container {}; - template<> struct build_container { diff --git a/include/iris/x4/traits/substitution.hpp b/include/iris/x4/traits/substitution.hpp index 896e12fcf..91745f3cd 100644 --- a/include/iris/x4/traits/substitution.hpp +++ b/include/iris/x4/traits/substitution.hpp @@ -21,9 +21,6 @@ namespace iris::x4::traits { -template -struct is_variant; - // Find out if T can be a (strong) substitute for Attribute template struct is_substitute; @@ -47,7 +44,7 @@ template struct is_all_substitute_for_tuple : std::false_type {}; template - requires is_same_size_sequence_v + requires is_same_size_tuple_like_v struct is_all_substitute_for_tuple : is_all_substitute_for_tuple_impl {}; template @@ -75,12 +72,6 @@ struct is_substitute_impl : value_type_is_substitute {}; -template - requires is_variant>::value -struct is_substitute_impl - : variant_has_substitute -{}; - } // detail template diff --git a/include/iris/x4/traits/tuple_traits.hpp b/include/iris/x4/traits/tuple_traits.hpp index 5f7810621..f481d89c2 100644 --- a/include/iris/x4/traits/tuple_traits.hpp +++ b/include/iris/x4/traits/tuple_traits.hpp @@ -14,78 +14,85 @@ #include -namespace iris::x4::traits { +namespace iris::x4 { -template -struct has_same_size - : std::bool_constant< - alloy::tuple_size_v> == - alloy::tuple_size_v> - > -{}; +namespace traits { template -constexpr bool has_same_size_v = has_same_size::value; +struct is_same_size_tuple_like_impl {}; -template -struct has_size - : std::bool_constant> == N> +template + requires alloy::is_tuple_like_v && alloy::is_tuple_like_v +struct is_same_size_tuple_like_impl + : std::bool_constant == alloy::tuple_size_v> {}; -template -constexpr bool has_size_v = has_size::value; - template -struct is_same_size_sequence +struct is_same_size_tuple_like : std::bool_constant>, - alloy::is_tuple_like>, - has_same_size + alloy::is_tuple_like, + alloy::is_tuple_like, + is_same_size_tuple_like_impl >> {}; template -constexpr bool is_same_size_sequence_v = is_same_size_sequence::value; +constexpr bool is_same_size_tuple_like_v = is_same_size_tuple_like::value; -template -struct is_size_one_sequence - : std::bool_constant>, - has_size - >> -{}; +template +struct is_single_element_tuple_like_impl {}; -template -constexpr bool is_size_one_sequence_v = is_size_one_sequence::value; +template + requires alloy::is_tuple_like_v +struct is_single_element_tuple_like_impl + : std::bool_constant == 1> +{}; -template -struct is_size_one_view +template +struct is_single_element_tuple_like : std::bool_constant>, - has_size + alloy::is_tuple_like, + is_single_element_tuple_like_impl >> {}; -template -constexpr bool is_size_one_view_v = is_size_one_view::value; +template +constexpr bool is_single_element_tuple_like_v = is_single_element_tuple_like::value; + +template +struct unwrap_single_element_tuple_like; +template +using unwrap_single_element_tuple_like_t = unwrap_single_element_tuple_like::type; template -struct synthesized_value +struct unwrap_single_element_tuple_like { using type = T; }; template -using synthesized_value_t = typename synthesized_value::type; + requires is_single_element_tuple_like_v +struct unwrap_single_element_tuple_like +{ + using type = alloy::tuple_element_t<0, T>; +}; + +} // triats template - requires is_size_one_sequence_v> -struct synthesized_value +constexpr T&& unwrap_single_element_tuple_like(T&& t) noexcept { - using type = std::remove_cvref_t>; -}; + return std::forward(t); +} + +template + requires traits::is_single_element_tuple_like_v> +constexpr auto&& unwrap_single_element_tuple_like(T&& t) // TODO: add noexcept +{ + return alloy::get<0>(std::forward(t)); +} -} // iris::x4::traits +} // iris::x4 #endif diff --git a/include/iris/x4/traits/variant_traits.hpp b/include/iris/x4/traits/variant_traits.hpp index 38615ee32..36650188b 100644 --- a/include/iris/x4/traits/variant_traits.hpp +++ b/include/iris/x4/traits/variant_traits.hpp @@ -115,7 +115,7 @@ struct variant_has_substitute template requires (!std::same_as, Attr>) struct variant_has_substitute, Attr> - : std::disjunction...> + : std::disjunction...> {}; } // iris::x4::traits diff --git a/test/x4/CMakeLists.txt b/test/x4/CMakeLists.txt index 47821e52d..4ce55f6c8 100644 --- a/test/x4/CMakeLists.txt +++ b/test/x4/CMakeLists.txt @@ -93,6 +93,7 @@ x4_define_tests( without x3_rule_problem alloy_wrong_substitute_test_case + single_element_tuple_like ) x4_define_test(rule_separate_tu rule_separate_tu.cpp rule_separate_tu_grammar.cpp) diff --git a/test/x4/single_element_tuple_like.cpp b/test/x4/single_element_tuple_like.cpp new file mode 100644 index 000000000..64f34e9ba --- /dev/null +++ b/test/x4/single_element_tuple_like.cpp @@ -0,0 +1,51 @@ +#include "iris_x4_test.hpp" + +#include + +#include +#include + +struct Ident { + std::string value; +}; + +struct Var { + Ident ident; +}; + +struct Decl { + Var var; +}; + +IRIS_ALLOY_ADAPT_STRUCT(Ident, value) +IRIS_ALLOY_ADAPT_STRUCT(Var, ident) +IRIS_ALLOY_ADAPT_STRUCT(Decl, var); + +using IdentRule = x4::rule; +using VarRule = x4::rule; +using DeclRule = x4::rule; + +constexpr IdentRule ident; +constexpr VarRule var; +constexpr DeclRule decl; + +IRIS_X4_DECLARE(IdentRule); +IRIS_X4_DECLARE(VarRule); +IRIS_X4_DECLARE(DeclRule) + +constexpr auto ident_def = (+x4::char_ >> x4::eps); +constexpr auto var_def = x4::lexeme[x4::lit('$') >> ident]; +constexpr auto decl_def = x4::skip(x4::space)[x4::lit("let") >> var]; + +IRIS_X4_DEFINE(ident); +IRIS_X4_DEFINE(var); +IRIS_X4_DEFINE(decl); + +TEST_CASE("single element tuple like") +{ + { + Decl attr; + CHECK(parse("let $foo", decl, attr)); + CHECK(attr.var.ident.value == "foo"); + } +}