From d83fdadd7d7b07baa453736efa25c9b7c36a4296 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Tue, 24 Feb 2026 15:59:01 +0900 Subject: [PATCH 01/24] Fix incorrectly swapped template arguments --- include/iris/x4/traits/substitution.hpp | 9 --------- include/iris/x4/traits/variant_traits.hpp | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/include/iris/x4/traits/substitution.hpp b/include/iris/x4/traits/substitution.hpp index 896e12fcf..054c7adcc 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; @@ -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/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 From 20713a7db21869d82fc973a87156af6ff867dda1 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Mon, 16 Feb 2026 18:22:16 +0900 Subject: [PATCH 02/24] WIP --- .../iris/x4/core/detail/parse_alternative.hpp | 20 ----- .../iris/x4/core/detail/parse_sequence.hpp | 56 ++++++++------ include/iris/x4/core/move_to.hpp | 73 +------------------ .../iris/x4/string/detail/string_parse.hpp | 10 +-- include/iris/x4/traits/tuple_traits.hpp | 17 ----- test/x4/attribute_type_check.cpp | 13 +--- test/x4/int.cpp | 8 -- test/x4/lit.cpp | 7 -- test/x4/rule4.cpp | 9 --- test/x4/sequence.cpp | 38 ---------- 10 files changed, 40 insertions(+), 211 deletions(-) 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_sequence.hpp b/include/iris/x4/core/detail/parse_sequence.hpp index eee6e0dcf..c0a26b723 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 @@ -96,6 +80,34 @@ struct pass_sequence_attribute : pass_sequence_attribute {}; +template +struct type_unwrap_size_one_tuple +{ + using type = T; +}; + +template +struct type_unwrap_size_one_tuple> +{ + using type = T; +}; + +template +using type_unwrap_size_one_tuple_t = type_unwrap_size_one_tuple::type; + +template +constexpr T&& unwrap_size_one_tuple(T&& t) noexcept +{ + return std::forward(t); +} + +template + requires traits::is_size_one_sequence_v +constexpr auto&& unwrap_size_one_tuple(T&& t) noexcept +{ + return alloy::get<0>(std::forward(t)); +} + template struct partition_attribute { @@ -126,21 +138,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 = type_unwrap_size_one_tuple_t>; + using r_part = type_unwrap_size_one_tuple_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 unwrap_size_one_tuple(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 unwrap_size_one_tuple(alloy::get<1>(alloy::tuple_split(alloy::tuple_ref(s)))); } }; diff --git a/include/iris/x4/core/move_to.hpp b/include/iris/x4/core/move_to.hpp index 61c81534c..d90140a42 100644 --- a/include/iris/x4/core/move_to.hpp +++ b/include/iris/x4/core/move_to.hpp @@ -116,18 +116,6 @@ 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) constexpr void move_to(Source&& src, Dest& dest) noexcept(std::is_nothrow_assignable_v) @@ -138,9 +126,7 @@ move_to(Source&& src, Dest& dest) } template Dest> - requires - traits::is_same_size_sequence_v && - (!traits::is_size_one_sequence_v) + requires traits::is_same_size_sequence_v constexpr void move_to(Source&& src, Dest& dest) noexcept(noexcept(alloy::tuple_assign(std::forward(src), dest))) @@ -153,42 +139,6 @@ move_to(Source&& src, Dest& dest) } template Dest> - requires traits::is_size_one_sequence_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); -} - -template Dest> - requires traits::is_size_one_sequence_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))))) -{ - 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))); -} - -template Dest> - requires (!traits::is_size_one_sequence_v) constexpr void move_to(Source&& src, Dest& dest) noexcept(std::is_nothrow_assignable_v) @@ -243,15 +193,6 @@ 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> @@ -285,18 +226,6 @@ move_to(Source&& src, Dest& dest) } } -// Size-one tuple-like forwarding -template Dest> - requires traits::is_size_one_sequence_v -constexpr void -move_to(Source&& src, Dest& dest) - noexcept(noexcept(x4::move_to(std::forward(src), alloy::get<0>(dest)))) -{ - static_assert(!std::same_as, Dest>, "[BUG] This call should instead resolve to the overload handling identical types"); - - x4::move_to(std::forward(src), alloy::get<0>(dest)); -} - template concept X4Movable = requires { x4::move_to(std::declval(), std::declval()); diff --git a/include/iris/x4/string/detail/string_parse.hpp b/include/iris/x4/string/detail/string_parse.hpp index 120a03ddd..1fdc1d958 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/tuple_traits.hpp b/include/iris/x4/traits/tuple_traits.hpp index 5f7810621..c48e557b0 100644 --- a/include/iris/x4/traits/tuple_traits.hpp +++ b/include/iris/x4/traits/tuple_traits.hpp @@ -69,23 +69,6 @@ struct is_size_one_view template constexpr bool is_size_one_view_v = is_size_one_view::value; - -template -struct synthesized_value -{ - using type = T; -}; - -template -using synthesized_value_t = typename synthesized_value::type; - -template - requires is_size_one_sequence_v> -struct synthesized_value -{ - using type = std::remove_cvref_t>; -}; - } // iris::x4::traits #endif diff --git a/test/x4/attribute_type_check.cpp b/test/x4/attribute_type_check.cpp index d70d218e6..b11f5b753 100644 --- a/test/x4/attribute_type_check.cpp +++ b/test/x4/attribute_type_check.cpp @@ -104,18 +104,7 @@ void gen_tests(Values const&... values) template void make_test(Attributes const&... attrs) { - // I would like to place all of this in a single call - // but it requires tremendous amount of heap to compile - gen_tests(attrs...); - gen_tests< - std::optional..., - alloy::tuple... - >(attrs..., attrs...); - - gen_tests< - std::optional>..., - alloy::tuple>... - >(alloy::tuple(attrs)..., attrs...); + gen_tests...>(attrs..., attrs...); } } // anonymous diff --git a/test/x4/int.cpp b/test/x4/int.cpp index 7a1cc7394..4a32d537e 100644 --- a/test/x4/int.cpp +++ b/test/x4/int.cpp @@ -230,12 +230,4 @@ TEST_CASE("int") int_parser int2; CHECK(parse("-12", int2, i)); } - - // single-element tuple tests - { - alloy::tuple i{}; - - REQUIRE(parse("-123456", int_, i)); - CHECK(alloy::get<0>(i) == -123456); - } } diff --git a/test/x4/lit.cpp b/test/x4/lit.cpp index ba370320a..cafcdc61e 100644 --- a/test/x4/lit.cpp +++ b/test/x4/lit.cpp @@ -145,11 +145,4 @@ TEST_CASE("lit") std::basic_string ws(L"kimpo"); CHECK(parse(L"kimpo", x4::standard_wide::string(ws))); } - - { - // single-element tuple tests - alloy::tuple s; - REQUIRE(parse("kimpo", x4::standard::string("kimpo"), s)); - CHECK(alloy::get<0>(s) == "kimpo"); - } } diff --git a/test/x4/rule4.cpp b/test/x4/rule4.cpp index 840a9a60f..43826acff 100644 --- a/test/x4/rule4.cpp +++ b/test/x4/rule4.cpp @@ -168,15 +168,6 @@ TEST_CASE("rule4") CHECK(*ov == 1); } - // test handling of single element tuple - { - auto r = rule>{} = int_; - - alloy::tuple v(0); - REQUIRE(parse("1", r, v)); - CHECK(alloy::get<0>(v) == 1); - } - // attribute compatibility test { constexpr auto expr = int_; diff --git a/test/x4/sequence.cpp b/test/x4/sequence.cpp index fc3628a70..884439793 100644 --- a/test/x4/sequence.cpp +++ b/test/x4/sequence.cpp @@ -114,44 +114,6 @@ TEST_CASE("sequence") CHECK(c == 'a'); } - { - // a single element tuple - alloy::tuple vec; - REQUIRE(parse("ab", char_ >> 'b', vec)); - CHECK(alloy::get<0>(vec) == 'a'); - } - - { - // Make sure single element tuples get passed through if the rhs - // has a single element tuple as its attribute. Edit JDG 2014: - // actually the issue here is that if the rhs in this case a rule - // (r), it should get it (i.e. the sequence parser should not - // unwrap it). It's odd that the RHS (r) does not really have a - // single element tuple, so the original comment is not accurate. - - using attr_type = alloy::tuple; - attr_type tpl; - - auto r = rule{} = char_ >> ',' >> int_; - - REQUIRE(parse("test:x,1", "test:" >> r, tpl)); - CHECK((tpl == attr_type('x', 1))); - } - - { - // make sure single element tuples get passed through if the rhs - // has a single element tuple as its attribute. This is a correction - // of the test above. - - using attr_type = alloy::tuple; - attr_type tpl; - - auto r = rule{} = int_; - - REQUIRE(parse("test:1", "test:" >> r, tpl)); - CHECK((tpl == attr_type(1))); - } - // unused means we don't care about the attribute CHECK(parse("abc", char_ >> 'b' >> char_, unused)); From 4c8e78beab0b654c1ec6cdcc03a70ff1d698978a Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Thu, 19 Feb 2026 02:40:18 +0900 Subject: [PATCH 03/24] Revert changes made to test code --- test/x4/attribute_type_check.cpp | 13 ++++++++++- test/x4/int.cpp | 8 +++++++ test/x4/lit.cpp | 7 ++++++ test/x4/rule4.cpp | 9 ++++++++ test/x4/sequence.cpp | 38 ++++++++++++++++++++++++++++++++ 5 files changed, 74 insertions(+), 1 deletion(-) diff --git a/test/x4/attribute_type_check.cpp b/test/x4/attribute_type_check.cpp index b11f5b753..d70d218e6 100644 --- a/test/x4/attribute_type_check.cpp +++ b/test/x4/attribute_type_check.cpp @@ -104,7 +104,18 @@ void gen_tests(Values const&... values) template void make_test(Attributes const&... attrs) { - gen_tests...>(attrs..., attrs...); + // I would like to place all of this in a single call + // but it requires tremendous amount of heap to compile + gen_tests(attrs...); + gen_tests< + std::optional..., + alloy::tuple... + >(attrs..., attrs...); + + gen_tests< + std::optional>..., + alloy::tuple>... + >(alloy::tuple(attrs)..., attrs...); } } // anonymous diff --git a/test/x4/int.cpp b/test/x4/int.cpp index 4a32d537e..7a1cc7394 100644 --- a/test/x4/int.cpp +++ b/test/x4/int.cpp @@ -230,4 +230,12 @@ TEST_CASE("int") int_parser int2; CHECK(parse("-12", int2, i)); } + + // single-element tuple tests + { + alloy::tuple i{}; + + REQUIRE(parse("-123456", int_, i)); + CHECK(alloy::get<0>(i) == -123456); + } } diff --git a/test/x4/lit.cpp b/test/x4/lit.cpp index cafcdc61e..ba370320a 100644 --- a/test/x4/lit.cpp +++ b/test/x4/lit.cpp @@ -145,4 +145,11 @@ TEST_CASE("lit") std::basic_string ws(L"kimpo"); CHECK(parse(L"kimpo", x4::standard_wide::string(ws))); } + + { + // single-element tuple tests + alloy::tuple s; + REQUIRE(parse("kimpo", x4::standard::string("kimpo"), s)); + CHECK(alloy::get<0>(s) == "kimpo"); + } } diff --git a/test/x4/rule4.cpp b/test/x4/rule4.cpp index 43826acff..840a9a60f 100644 --- a/test/x4/rule4.cpp +++ b/test/x4/rule4.cpp @@ -168,6 +168,15 @@ TEST_CASE("rule4") CHECK(*ov == 1); } + // test handling of single element tuple + { + auto r = rule>{} = int_; + + alloy::tuple v(0); + REQUIRE(parse("1", r, v)); + CHECK(alloy::get<0>(v) == 1); + } + // attribute compatibility test { constexpr auto expr = int_; diff --git a/test/x4/sequence.cpp b/test/x4/sequence.cpp index 884439793..fc3628a70 100644 --- a/test/x4/sequence.cpp +++ b/test/x4/sequence.cpp @@ -114,6 +114,44 @@ TEST_CASE("sequence") CHECK(c == 'a'); } + { + // a single element tuple + alloy::tuple vec; + REQUIRE(parse("ab", char_ >> 'b', vec)); + CHECK(alloy::get<0>(vec) == 'a'); + } + + { + // Make sure single element tuples get passed through if the rhs + // has a single element tuple as its attribute. Edit JDG 2014: + // actually the issue here is that if the rhs in this case a rule + // (r), it should get it (i.e. the sequence parser should not + // unwrap it). It's odd that the RHS (r) does not really have a + // single element tuple, so the original comment is not accurate. + + using attr_type = alloy::tuple; + attr_type tpl; + + auto r = rule{} = char_ >> ',' >> int_; + + REQUIRE(parse("test:x,1", "test:" >> r, tpl)); + CHECK((tpl == attr_type('x', 1))); + } + + { + // make sure single element tuples get passed through if the rhs + // has a single element tuple as its attribute. This is a correction + // of the test above. + + using attr_type = alloy::tuple; + attr_type tpl; + + auto r = rule{} = int_; + + REQUIRE(parse("test:1", "test:" >> r, tpl)); + CHECK((tpl == attr_type(1))); + } + // unused means we don't care about the attribute CHECK(parse("abc", char_ >> 'b' >> char_, unused)); From 55939397b7b6750500af1736718623defb0ab535 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Thu, 19 Feb 2026 02:41:18 +0900 Subject: [PATCH 04/24] Revert "Revert changes made to test code" This reverts commit e0344ff317c9f744d1c32d193f63a3ef46351a1d. --- test/x4/attribute_type_check.cpp | 13 +---------- test/x4/int.cpp | 8 ------- test/x4/lit.cpp | 7 ------ test/x4/rule4.cpp | 9 -------- test/x4/sequence.cpp | 38 -------------------------------- 5 files changed, 1 insertion(+), 74 deletions(-) diff --git a/test/x4/attribute_type_check.cpp b/test/x4/attribute_type_check.cpp index d70d218e6..b11f5b753 100644 --- a/test/x4/attribute_type_check.cpp +++ b/test/x4/attribute_type_check.cpp @@ -104,18 +104,7 @@ void gen_tests(Values const&... values) template void make_test(Attributes const&... attrs) { - // I would like to place all of this in a single call - // but it requires tremendous amount of heap to compile - gen_tests(attrs...); - gen_tests< - std::optional..., - alloy::tuple... - >(attrs..., attrs...); - - gen_tests< - std::optional>..., - alloy::tuple>... - >(alloy::tuple(attrs)..., attrs...); + gen_tests...>(attrs..., attrs...); } } // anonymous diff --git a/test/x4/int.cpp b/test/x4/int.cpp index 7a1cc7394..4a32d537e 100644 --- a/test/x4/int.cpp +++ b/test/x4/int.cpp @@ -230,12 +230,4 @@ TEST_CASE("int") int_parser int2; CHECK(parse("-12", int2, i)); } - - // single-element tuple tests - { - alloy::tuple i{}; - - REQUIRE(parse("-123456", int_, i)); - CHECK(alloy::get<0>(i) == -123456); - } } diff --git a/test/x4/lit.cpp b/test/x4/lit.cpp index ba370320a..cafcdc61e 100644 --- a/test/x4/lit.cpp +++ b/test/x4/lit.cpp @@ -145,11 +145,4 @@ TEST_CASE("lit") std::basic_string ws(L"kimpo"); CHECK(parse(L"kimpo", x4::standard_wide::string(ws))); } - - { - // single-element tuple tests - alloy::tuple s; - REQUIRE(parse("kimpo", x4::standard::string("kimpo"), s)); - CHECK(alloy::get<0>(s) == "kimpo"); - } } diff --git a/test/x4/rule4.cpp b/test/x4/rule4.cpp index 840a9a60f..43826acff 100644 --- a/test/x4/rule4.cpp +++ b/test/x4/rule4.cpp @@ -168,15 +168,6 @@ TEST_CASE("rule4") CHECK(*ov == 1); } - // test handling of single element tuple - { - auto r = rule>{} = int_; - - alloy::tuple v(0); - REQUIRE(parse("1", r, v)); - CHECK(alloy::get<0>(v) == 1); - } - // attribute compatibility test { constexpr auto expr = int_; diff --git a/test/x4/sequence.cpp b/test/x4/sequence.cpp index fc3628a70..884439793 100644 --- a/test/x4/sequence.cpp +++ b/test/x4/sequence.cpp @@ -114,44 +114,6 @@ TEST_CASE("sequence") CHECK(c == 'a'); } - { - // a single element tuple - alloy::tuple vec; - REQUIRE(parse("ab", char_ >> 'b', vec)); - CHECK(alloy::get<0>(vec) == 'a'); - } - - { - // Make sure single element tuples get passed through if the rhs - // has a single element tuple as its attribute. Edit JDG 2014: - // actually the issue here is that if the rhs in this case a rule - // (r), it should get it (i.e. the sequence parser should not - // unwrap it). It's odd that the RHS (r) does not really have a - // single element tuple, so the original comment is not accurate. - - using attr_type = alloy::tuple; - attr_type tpl; - - auto r = rule{} = char_ >> ',' >> int_; - - REQUIRE(parse("test:x,1", "test:" >> r, tpl)); - CHECK((tpl == attr_type('x', 1))); - } - - { - // make sure single element tuples get passed through if the rhs - // has a single element tuple as its attribute. This is a correction - // of the test above. - - using attr_type = alloy::tuple; - attr_type tpl; - - auto r = rule{} = int_; - - REQUIRE(parse("test:1", "test:" >> r, tpl)); - CHECK((tpl == attr_type(1))); - } - // unused means we don't care about the attribute CHECK(parse("abc", char_ >> 'b' >> char_, unused)); From b64f47da8f17bc77ccc0f6c2677c98fa5fcc19ad Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Thu, 19 Feb 2026 03:56:18 +0900 Subject: [PATCH 05/24] Split RuleAttrCompatible overload --- include/iris/x4/rule.hpp | 69 +++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/include/iris/x4/rule.hpp b/include/iris/x4/rule.hpp index 2f58af834..3ade5fa6f 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::RuleAttrNeedsNarrowingConversion); - // 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; + } + + // TODO: 良いコメントを書く + 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::RuleAttrNeedsNarrowingConversion [[nodiscard]] constexpr bool parse(It&, Se const&, Context const&, Exposed&) const = delete; // Rule attribute needs narrowing conversion From 7e59b2d5b8440e4448a9dc724c0ef155ad6b45a6 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Thu, 19 Feb 2026 05:25:50 +0900 Subject: [PATCH 06/24] Refactor `attribute_of_binary` and add unwrapping behavior --- include/iris/x4/operator/alternative.hpp | 2 +- include/iris/x4/operator/sequence.hpp | 2 +- .../iris/x4/traits/attribute_of_binary.hpp | 107 +++++++++++------- 3 files changed, 66 insertions(+), 45 deletions(-) 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/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 From 9aae184ac9dad553447a83503b349334dac1b759 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Thu, 19 Feb 2026 05:33:25 +0900 Subject: [PATCH 07/24] MERGE CAREFULLY --- include/iris/x4/rule.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/iris/x4/rule.hpp b/include/iris/x4/rule.hpp index 3ade5fa6f..095333c9f 100644 --- a/include/iris/x4/rule.hpp +++ b/include/iris/x4/rule.hpp @@ -476,7 +476,7 @@ struct rule : parser> (!std::same_as, unused_type>) && (!std::same_as, RuleAttr>) && (!detail::RuleAttrTransformable) && - detail::RuleAttrNeedsNarrowingConversion + (detail::RuleAttrConvertible && !detail::RuleAttrConvertibleWithoutNarrowing) [[nodiscard]] constexpr bool parse(It&, Se const&, Context const&, Exposed&) const = delete; // Rule attribute needs narrowing conversion From 567f30ddc1cc7bb7d1eacb6953704a718e0b89d4 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Thu, 19 Feb 2026 05:35:12 +0900 Subject: [PATCH 08/24] BUILD BEFORE PUSH --- include/iris/x4/rule.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/iris/x4/rule.hpp b/include/iris/x4/rule.hpp index 095333c9f..02d9aa4e4 100644 --- a/include/iris/x4/rule.hpp +++ b/include/iris/x4/rule.hpp @@ -431,7 +431,7 @@ struct rule : parser> using detail::parse_rule; // ADL - static_assert(!detail::RuleAttrNeedsNarrowingConversion); + static_assert(detail::RuleAttrConvertibleWithoutNarrowing); // TODO: specialize `container_appender` case, do not create temporary From 5c89a547508818649e9d413a3e3fef92ed522911 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Thu, 19 Feb 2026 11:39:38 +0900 Subject: [PATCH 09/24] Revert changes made to move_to --- include/iris/x4/core/move_to.hpp | 73 +++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/include/iris/x4/core/move_to.hpp b/include/iris/x4/core/move_to.hpp index d90140a42..61c81534c 100644 --- a/include/iris/x4/core/move_to.hpp +++ b/include/iris/x4/core/move_to.hpp @@ -116,6 +116,18 @@ 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) constexpr void move_to(Source&& src, Dest& dest) noexcept(std::is_nothrow_assignable_v) @@ -126,7 +138,9 @@ move_to(Source&& src, Dest& dest) } template Dest> - requires traits::is_same_size_sequence_v + requires + traits::is_same_size_sequence_v && + (!traits::is_size_one_sequence_v) constexpr void move_to(Source&& src, Dest& dest) noexcept(noexcept(alloy::tuple_assign(std::forward(src), dest))) @@ -139,6 +153,42 @@ move_to(Source&& src, Dest& dest) } template Dest> + requires traits::is_size_one_sequence_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); +} + +template Dest> + requires traits::is_size_one_sequence_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))))) +{ + 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))); +} + +template Dest> + requires (!traits::is_size_one_sequence_v) constexpr void move_to(Source&& src, Dest& dest) noexcept(std::is_nothrow_assignable_v) @@ -193,6 +243,15 @@ 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> @@ -226,6 +285,18 @@ move_to(Source&& src, Dest& dest) } } +// Size-one tuple-like forwarding +template Dest> + requires traits::is_size_one_sequence_v +constexpr void +move_to(Source&& src, Dest& dest) + noexcept(noexcept(x4::move_to(std::forward(src), alloy::get<0>(dest)))) +{ + static_assert(!std::same_as, Dest>, "[BUG] This call should instead resolve to the overload handling identical types"); + + x4::move_to(std::forward(src), alloy::get<0>(dest)); +} + template concept X4Movable = requires { x4::move_to(std::declval(), std::declval()); From fa2731065d534178e486da978e680870555ae252 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Thu, 19 Feb 2026 11:51:30 +0900 Subject: [PATCH 10/24] Remove build_container specialization for single element tuple --- include/iris/x4/traits/container_traits.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/iris/x4/traits/container_traits.hpp b/include/iris/x4/traits/container_traits.hpp index c5c9b28fc..2b3f527d1 100644 --- a/include/iris/x4/traits/container_traits.hpp +++ b/include/iris/x4/traits/container_traits.hpp @@ -471,9 +471,6 @@ struct build_container template using build_container_t = typename build_container::type; -template -struct build_container> : build_container {}; - template<> struct build_container { From d597691605af7955d3612fa179af9edbe24796ad Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Thu, 19 Feb 2026 16:20:11 +0900 Subject: [PATCH 11/24] Make `attribute_category` of single element tuple-like "fallthrough" --- .../x4/core/detail/parse_into_container.hpp | 2 +- .../iris/x4/core/detail/parse_sequence.hpp | 2 +- include/iris/x4/core/move_to.hpp | 78 ++++--------------- .../iris/x4/string/detail/string_parse.hpp | 4 +- include/iris/x4/traits/attribute_category.hpp | 14 +++- include/iris/x4/traits/container_traits.hpp | 3 - include/iris/x4/traits/substitution.hpp | 2 +- include/iris/x4/traits/tuple_traits.hpp | 74 ++++++++++-------- 8 files changed, 72 insertions(+), 107 deletions(-) diff --git a/include/iris/x4/core/detail/parse_into_container.hpp b/include/iris/x4/core/detail/parse_into_container.hpp index 8a40b2493..f6f2f3080 100644 --- a/include/iris/x4/core/detail/parse_into_container.hpp +++ b/include/iris/x4/core/detail/parse_into_container.hpp @@ -113,7 +113,7 @@ struct parse_into_container_base_impl 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"); + static_assert(traits::is_single_element_tuple_like_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)); } diff --git a/include/iris/x4/core/detail/parse_sequence.hpp b/include/iris/x4/core/detail/parse_sequence.hpp index c0a26b723..014ffd68f 100644 --- a/include/iris/x4/core/detail/parse_sequence.hpp +++ b/include/iris/x4/core/detail/parse_sequence.hpp @@ -102,7 +102,7 @@ constexpr T&& unwrap_size_one_tuple(T&& t) noexcept } template - requires traits::is_size_one_sequence_v + requires traits::is_single_element_tuple_like_v constexpr auto&& unwrap_size_one_tuple(T&& t) noexcept { return alloy::get<0>(std::forward(t)); diff --git a/include/iris/x4/core/move_to.hpp b/include/iris/x4/core/move_to.hpp index 61c81534c..f78f7ed5e 100644 --- a/include/iris/x4/core/move_to.hpp +++ b/include/iris/x4/core/move_to.hpp @@ -116,18 +116,6 @@ 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) constexpr void move_to(Source&& src, Dest& dest) noexcept(std::is_nothrow_assignable_v) @@ -139,8 +127,7 @@ 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 constexpr void move_to(Source&& src, Dest& dest) noexcept(noexcept(alloy::tuple_assign(std::forward(src), dest))) @@ -153,42 +140,7 @@ move_to(Source&& src, Dest& dest) } template Dest> - requires traits::is_size_one_sequence_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); -} - -template Dest> - requires traits::is_size_one_sequence_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))))) -{ - 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))); -} - -template Dest> - requires (!traits::is_size_one_sequence_v) + // requires traits::variant_has_substitute_v constexpr void move_to(Source&& src, Dest& dest) noexcept(std::is_nothrow_assignable_v) @@ -214,6 +166,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,15 +196,6 @@ 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> @@ -285,9 +229,19 @@ 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(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) noexcept(noexcept(x4::move_to(std::forward(src), alloy::get<0>(dest)))) diff --git a/include/iris/x4/string/detail/string_parse.hpp b/include/iris/x4/string/detail/string_parse.hpp index 1fdc1d958..64d78650c 100644 --- a/include/iris/x4/string/detail/string_parse.hpp +++ b/include/iris/x4/string/detail/string_parse.hpp @@ -31,7 +31,7 @@ string_parse( ) noexcept(std::same_as, unused_container_type>) { static_assert(std::same_as, traits::container_attr>); - using value_type = traits::container_value_t; + 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,7 +77,7 @@ string_parse( ) noexcept(std::same_as, unused_container_type>) { static_assert(std::same_as, traits::container_attr>); - using value_type = traits::container_value_t; + 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/container_traits.hpp b/include/iris/x4/traits/container_traits.hpp index 2b3f527d1..5c2d8d1f0 100644 --- a/include/iris/x4/traits/container_traits.hpp +++ b/include/iris/x4/traits/container_traits.hpp @@ -14,9 +14,6 @@ #include #include -#include -#include - #include #include #include diff --git a/include/iris/x4/traits/substitution.hpp b/include/iris/x4/traits/substitution.hpp index 054c7adcc..91745f3cd 100644 --- a/include/iris/x4/traits/substitution.hpp +++ b/include/iris/x4/traits/substitution.hpp @@ -44,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 diff --git a/include/iris/x4/traits/tuple_traits.hpp b/include/iris/x4/traits/tuple_traits.hpp index c48e557b0..46108bf8b 100644 --- a/include/iris/x4/traits/tuple_traits.hpp +++ b/include/iris/x4/traits/tuple_traits.hpp @@ -17,57 +17,63 @@ namespace iris::x4::traits { template -struct has_same_size - : std::bool_constant< - alloy::tuple_size_v> == - alloy::tuple_size_v> - > -{}; +struct is_same_size_tuple_like_impl {}; template -constexpr bool has_same_size_v = has_same_size::value; - -template -struct has_size - : std::bool_constant> == N> + 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 unwrap_single_element_tuple_like +{ + using type = T; +}; + +template + requires is_single_element_tuple_like_v +struct unwrap_single_element_tuple_like + : unwrap_single_element_tuple_like> +{}; } // iris::x4::traits From 37b3151501c3d26743245b0a361e92b4c576bada Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 20 Feb 2026 02:11:53 +0900 Subject: [PATCH 12/24] Fix ambiguity --- .../iris/x4/core/detail/parse_sequence.hpp | 29 +++++-------------- include/iris/x4/core/move_to.hpp | 15 +++++++--- include/iris/x4/traits/container_traits.hpp | 8 +++++ 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/include/iris/x4/core/detail/parse_sequence.hpp b/include/iris/x4/core/detail/parse_sequence.hpp index 014ffd68f..421aa3f67 100644 --- a/include/iris/x4/core/detail/parse_sequence.hpp +++ b/include/iris/x4/core/detail/parse_sequence.hpp @@ -81,31 +81,16 @@ struct pass_sequence_attribute {}; template -struct type_unwrap_size_one_tuple -{ - using type = T; -}; - -template -struct type_unwrap_size_one_tuple> -{ - using type = T; -}; - -template -using type_unwrap_size_one_tuple_t = type_unwrap_size_one_tuple::type; - -template -constexpr T&& unwrap_size_one_tuple(T&& t) noexcept +constexpr T&& do_unwrap_size_one_tuple(T&& t) noexcept { return std::forward(t); } template requires traits::is_single_element_tuple_like_v -constexpr auto&& unwrap_size_one_tuple(T&& t) noexcept +constexpr auto&& do_unwrap_size_one_tuple(T&& t) noexcept { - return alloy::get<0>(std::forward(t)); + return do_unwrap_size_one_tuple(alloy::get<0>(std::forward(t))); } template @@ -138,21 +123,21 @@ struct partition_attribute using view = alloy::tuple_ref_t; using splitted = alloy::tuple_split_t; - using l_part = type_unwrap_size_one_tuple_t>; - using r_part = type_unwrap_size_one_tuple_t>; + 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 unwrap_size_one_tuple(alloy::get<0>(alloy::tuple_split(alloy::tuple_ref(s)))); + return do_unwrap_size_one_tuple(alloy::get<0>(alloy::tuple_split(alloy::tuple_ref(s)))); } [[nodiscard]] static constexpr r_part right(Attr& s) // TODO: noexcept { - return unwrap_size_one_tuple(alloy::get<1>(alloy::tuple_split(alloy::tuple_ref(s)))); + return do_unwrap_size_one_tuple(alloy::get<1>(alloy::tuple_split(alloy::tuple_ref(s)))); } }; diff --git a/include/iris/x4/core/move_to.hpp b/include/iris/x4/core/move_to.hpp index f78f7ed5e..70d2fef84 100644 --- a/include/iris/x4/core/move_to.hpp +++ b/include/iris/x4/core/move_to.hpp @@ -116,6 +116,7 @@ constexpr void move_to(It const&, Se const&, unused_type const&&) = delete; // t // Category specific -------------------------------------- template Dest> + requires (!traits::is_single_element_tuple_like_v) constexpr void move_to(Source&& src, Dest& dest) noexcept(std::is_nothrow_assignable_v) @@ -127,7 +128,8 @@ move_to(Source&& src, Dest& dest) template Dest> requires - traits::is_same_size_tuple_like_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))) @@ -140,7 +142,9 @@ move_to(Source&& src, Dest& dest) } template Dest> - // requires traits::variant_has_substitute_v + requires +// traits::variant_has_substitute_v && // TODO: investigate compilation error due to existance of this constraint + (!traits::is_single_element_tuple_like_v) constexpr void move_to(Source&& src, Dest& dest) noexcept(std::is_nothrow_assignable_v) @@ -151,6 +155,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) @@ -166,7 +171,7 @@ template struct container_appender; template Se, traits::CategorizedAttr Dest> - requires (!traits::is_single_element_tuple_like_v) + requires (!traits::is_single_element_tuple_like_v) constexpr void move_to(It first, Se last, Dest& dest) // never noexcept, requires container insertion @@ -201,7 +206,8 @@ move_to(It first, Se last, std::ranges::subrange& rng) 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) @@ -216,6 +222,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 diff --git a/include/iris/x4/traits/container_traits.hpp b/include/iris/x4/traits/container_traits.hpp index 5c2d8d1f0..3a19a29d1 100644 --- a/include/iris/x4/traits/container_traits.hpp +++ b/include/iris/x4/traits/container_traits.hpp @@ -12,7 +12,9 @@ =============================================================================*/ #include + #include +#include #include #include @@ -84,6 +86,12 @@ struct container_value template using container_value_t = typename container_value::type; +template + requires traits::is_single_element_tuple_like_v +struct container_value + : container_value> +{}; + template struct container_value : container_value {}; From 397dd7e8cae35ecf522a8aad86c65210db98b9ae Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 20 Feb 2026 02:44:16 +0900 Subject: [PATCH 13/24] Reapply "Revert changes made to test code" This reverts commit f4e1a9460fc172a683771103be5185038dfb9e4b. --- test/x4/attribute_type_check.cpp | 13 ++++++++++- test/x4/int.cpp | 8 +++++++ test/x4/lit.cpp | 7 ++++++ test/x4/rule4.cpp | 9 ++++++++ test/x4/sequence.cpp | 38 ++++++++++++++++++++++++++++++++ 5 files changed, 74 insertions(+), 1 deletion(-) diff --git a/test/x4/attribute_type_check.cpp b/test/x4/attribute_type_check.cpp index b11f5b753..d70d218e6 100644 --- a/test/x4/attribute_type_check.cpp +++ b/test/x4/attribute_type_check.cpp @@ -104,7 +104,18 @@ void gen_tests(Values const&... values) template void make_test(Attributes const&... attrs) { - gen_tests...>(attrs..., attrs...); + // I would like to place all of this in a single call + // but it requires tremendous amount of heap to compile + gen_tests(attrs...); + gen_tests< + std::optional..., + alloy::tuple... + >(attrs..., attrs...); + + gen_tests< + std::optional>..., + alloy::tuple>... + >(alloy::tuple(attrs)..., attrs...); } } // anonymous diff --git a/test/x4/int.cpp b/test/x4/int.cpp index 4a32d537e..7a1cc7394 100644 --- a/test/x4/int.cpp +++ b/test/x4/int.cpp @@ -230,4 +230,12 @@ TEST_CASE("int") int_parser int2; CHECK(parse("-12", int2, i)); } + + // single-element tuple tests + { + alloy::tuple i{}; + + REQUIRE(parse("-123456", int_, i)); + CHECK(alloy::get<0>(i) == -123456); + } } diff --git a/test/x4/lit.cpp b/test/x4/lit.cpp index cafcdc61e..ba370320a 100644 --- a/test/x4/lit.cpp +++ b/test/x4/lit.cpp @@ -145,4 +145,11 @@ TEST_CASE("lit") std::basic_string ws(L"kimpo"); CHECK(parse(L"kimpo", x4::standard_wide::string(ws))); } + + { + // single-element tuple tests + alloy::tuple s; + REQUIRE(parse("kimpo", x4::standard::string("kimpo"), s)); + CHECK(alloy::get<0>(s) == "kimpo"); + } } diff --git a/test/x4/rule4.cpp b/test/x4/rule4.cpp index 43826acff..840a9a60f 100644 --- a/test/x4/rule4.cpp +++ b/test/x4/rule4.cpp @@ -168,6 +168,15 @@ TEST_CASE("rule4") CHECK(*ov == 1); } + // test handling of single element tuple + { + auto r = rule>{} = int_; + + alloy::tuple v(0); + REQUIRE(parse("1", r, v)); + CHECK(alloy::get<0>(v) == 1); + } + // attribute compatibility test { constexpr auto expr = int_; diff --git a/test/x4/sequence.cpp b/test/x4/sequence.cpp index 884439793..fc3628a70 100644 --- a/test/x4/sequence.cpp +++ b/test/x4/sequence.cpp @@ -114,6 +114,44 @@ TEST_CASE("sequence") CHECK(c == 'a'); } + { + // a single element tuple + alloy::tuple vec; + REQUIRE(parse("ab", char_ >> 'b', vec)); + CHECK(alloy::get<0>(vec) == 'a'); + } + + { + // Make sure single element tuples get passed through if the rhs + // has a single element tuple as its attribute. Edit JDG 2014: + // actually the issue here is that if the rhs in this case a rule + // (r), it should get it (i.e. the sequence parser should not + // unwrap it). It's odd that the RHS (r) does not really have a + // single element tuple, so the original comment is not accurate. + + using attr_type = alloy::tuple; + attr_type tpl; + + auto r = rule{} = char_ >> ',' >> int_; + + REQUIRE(parse("test:x,1", "test:" >> r, tpl)); + CHECK((tpl == attr_type('x', 1))); + } + + { + // make sure single element tuples get passed through if the rhs + // has a single element tuple as its attribute. This is a correction + // of the test above. + + using attr_type = alloy::tuple; + attr_type tpl; + + auto r = rule{} = int_; + + REQUIRE(parse("test:1", "test:" >> r, tpl)); + CHECK((tpl == attr_type(1))); + } + // unused means we don't care about the attribute CHECK(parse("abc", char_ >> 'b' >> char_, unused)); From 95c968daf50beaa8f4fa0a2930c95fb28adcdb01 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 20 Feb 2026 03:27:56 +0900 Subject: [PATCH 14/24] Add unwrapping utility, temporally disable alternative test --- .../x4/core/detail/parse_into_container.hpp | 28 ++++--------------- .../core/unwrap_single_element_tuple_like.hpp | 25 +++++++++++++++++ test/x4/CMakeLists.txt | 2 +- 3 files changed, 32 insertions(+), 23 deletions(-) create mode 100644 include/iris/x4/core/unwrap_single_element_tuple_like.hpp diff --git a/include/iris/x4/core/detail/parse_into_container.hpp b/include/iris/x4/core/detail/parse_into_container.hpp index f6f2f3080..270ea39c9 100644 --- a/include/iris/x4/core/detail/parse_into_container.hpp +++ b/include/iris/x4/core/detail/parse_into_container.hpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -39,7 +40,7 @@ struct parse_into_container_base_impl { // Parser has attribute (synthesize; Attribute is a container) template Se, class Context, X4Attribute Attr> - requires (!parser_accepts_container_v>) + requires (!parser_accepts_container_v>>) [[nodiscard]] static constexpr bool call_synthesize( Parser const& parser, It& first, Se const& last, @@ -48,14 +49,14 @@ struct parse_into_container_base_impl { static_assert(!std::same_as, unused_container_type>); - using value_type = traits::container_value_t>; + using value_type = traits::container_value_t>>; value_type val; // default-initialize //static_assert(Parsable); if (!parser.parse(first, last, ctx, val)) return false; // push the parsed value into our attribute - traits::push_back(unwrap_recursive(attr), std::move(val)); + traits::push_back(x4::unwrap_single_element_tuple_like(unwrap_recursive(attr)), std::move(val)); return true; } @@ -87,11 +88,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, @@ -102,22 +102,6 @@ struct parse_into_container_base_impl 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::is_single_element_tuple_like_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)); - } - // Parser has no attribute (pass unused) template Se, class Context, X4Attribute Attr> requires (!has_attribute_v) diff --git a/include/iris/x4/core/unwrap_single_element_tuple_like.hpp b/include/iris/x4/core/unwrap_single_element_tuple_like.hpp new file mode 100644 index 000000000..a48cf3be6 --- /dev/null +++ b/include/iris/x4/core/unwrap_single_element_tuple_like.hpp @@ -0,0 +1,25 @@ +#ifndef IRIS_X4_CORE_UNWRAP_SINGLE_ELEMENT_TUPLE_LIKE_HPP +#define IRIS_X4_CORE_UNWRAP_SINGLE_ELEMENT_TUPLE_LIKE_HPP + +#include + +#include + +namespace iris::x4 { + +template +constexpr T&& unwrap_single_element_tuple_like(T&& t) noexcept +{ + return std::forward(t); +} + +template + requires traits::is_single_element_tuple_like_v> +constexpr auto&& unwrap_single_element_tuple_like(T&& t) noexcept(noexcept(alloy::get<0>(std::forward(t)))) +{ + return x4::unwrap_single_element_tuple_like(alloy::get<0>(std::forward(t))); +} + +} // iris::x4 + +#endif diff --git a/test/x4/CMakeLists.txt b/test/x4/CMakeLists.txt index 47821e52d..204575738 100644 --- a/test/x4/CMakeLists.txt +++ b/test/x4/CMakeLists.txt @@ -34,7 +34,7 @@ endfunction() x4_define_tests( actions - alternative +# alternative and_predicate as attr From 3da86c47585f8c33024b7ed76e87667dd626de49 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 20 Feb 2026 06:16:58 +0900 Subject: [PATCH 15/24] Unwrap --- include/iris/x4/core/detail/parse_into_container.hpp | 9 ++++++--- test/x4/CMakeLists.txt | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/include/iris/x4/core/detail/parse_into_container.hpp b/include/iris/x4/core/detail/parse_into_container.hpp index 270ea39c9..c91c750d5 100644 --- a/include/iris/x4/core/detail/parse_into_container.hpp +++ b/include/iris/x4/core/detail/parse_into_container.hpp @@ -196,15 +196,18 @@ parse_into_container( "`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/test/x4/CMakeLists.txt b/test/x4/CMakeLists.txt index 204575738..47821e52d 100644 --- a/test/x4/CMakeLists.txt +++ b/test/x4/CMakeLists.txt @@ -34,7 +34,7 @@ endfunction() x4_define_tests( actions -# alternative + alternative and_predicate as attr From ca22a6bfd667b074c405a6377d6104df15395c23 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 20 Feb 2026 12:08:31 +0900 Subject: [PATCH 16/24] Add single element tuple like test --- .../x4/core/detail/parse_into_container.hpp | 8 ++-- .../iris/x4/core/detail/parse_sequence.hpp | 17 +------- include/iris/x4/core/move_to.hpp | 3 +- .../core/unwrap_single_element_tuple_like.hpp | 8 ++-- include/iris/x4/traits/tuple_traits.hpp | 5 ++- test/x4/CMakeLists.txt | 1 + test/x4/single_element_tuple_like.cpp | 42 +++++++++++++++++++ 7 files changed, 58 insertions(+), 26 deletions(-) create mode 100644 test/x4/single_element_tuple_like.cpp diff --git a/include/iris/x4/core/detail/parse_into_container.hpp b/include/iris/x4/core/detail/parse_into_container.hpp index c91c750d5..f4c1a9463 100644 --- a/include/iris/x4/core/detail/parse_into_container.hpp +++ b/include/iris/x4/core/detail/parse_into_container.hpp @@ -40,7 +40,7 @@ struct parse_into_container_base_impl { // Parser has attribute (synthesize; Attribute is a container) template Se, class Context, X4Attribute Attr> - requires (!parser_accepts_container_v>>) + requires (!parser_accepts_container_v>) [[nodiscard]] static constexpr bool call_synthesize( Parser const& parser, It& first, Se const& last, @@ -49,14 +49,14 @@ struct parse_into_container_base_impl { static_assert(!std::same_as, unused_container_type>); - using value_type = traits::container_value_t>>; + using value_type = traits::container_value_t>; value_type val; // default-initialize //static_assert(Parsable); if (!parser.parse(first, last, ctx, val)) return false; // push the parsed value into our attribute - traits::push_back(x4::unwrap_single_element_tuple_like(unwrap_recursive(attr)), std::move(val)); + traits::push_back(unwrap_recursive(attr), std::move(val)); return true; } @@ -99,7 +99,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); + 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) diff --git a/include/iris/x4/core/detail/parse_sequence.hpp b/include/iris/x4/core/detail/parse_sequence.hpp index 421aa3f67..1b3cf3e2b 100644 --- a/include/iris/x4/core/detail/parse_sequence.hpp +++ b/include/iris/x4/core/detail/parse_sequence.hpp @@ -80,19 +80,6 @@ struct pass_sequence_attribute : pass_sequence_attribute {}; -template -constexpr T&& do_unwrap_size_one_tuple(T&& t) noexcept -{ - return std::forward(t); -} - -template - requires traits::is_single_element_tuple_like_v -constexpr auto&& do_unwrap_size_one_tuple(T&& t) noexcept -{ - return do_unwrap_size_one_tuple(alloy::get<0>(std::forward(t))); -} - template struct partition_attribute { @@ -131,13 +118,13 @@ struct partition_attribute [[nodiscard]] static constexpr l_part left(Attr& s) // TODO: noexcept { - return do_unwrap_size_one_tuple(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 do_unwrap_size_one_tuple(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)))); } }; diff --git a/include/iris/x4/core/move_to.hpp b/include/iris/x4/core/move_to.hpp index 70d2fef84..7bf8b27b1 100644 --- a/include/iris/x4/core/move_to.hpp +++ b/include/iris/x4/core/move_to.hpp @@ -251,7 +251,8 @@ template 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)))) + // TODO: investigate wierd compilation error due to noexcept specification + // noexcept(noexcept(x4::move_to(std::forward(src), alloy::get<0>(dest)))) { static_assert(!std::same_as, Dest>, "[BUG] This call should instead resolve to the overload handling identical types"); diff --git a/include/iris/x4/core/unwrap_single_element_tuple_like.hpp b/include/iris/x4/core/unwrap_single_element_tuple_like.hpp index a48cf3be6..0f186bf40 100644 --- a/include/iris/x4/core/unwrap_single_element_tuple_like.hpp +++ b/include/iris/x4/core/unwrap_single_element_tuple_like.hpp @@ -3,7 +3,7 @@ #include -#include +#include namespace iris::x4 { @@ -15,11 +15,11 @@ constexpr T&& unwrap_single_element_tuple_like(T&& t) noexcept template requires traits::is_single_element_tuple_like_v> -constexpr auto&& unwrap_single_element_tuple_like(T&& t) noexcept(noexcept(alloy::get<0>(std::forward(t)))) +constexpr auto&& unwrap_single_element_tuple_like(T&& t) // TODO: add noexcept { - return x4::unwrap_single_element_tuple_like(alloy::get<0>(std::forward(t))); + return alloy::get<0>(std::forward(t)); } -} // iris::x4 +} #endif diff --git a/include/iris/x4/traits/tuple_traits.hpp b/include/iris/x4/traits/tuple_traits.hpp index 46108bf8b..8b6291258 100644 --- a/include/iris/x4/traits/tuple_traits.hpp +++ b/include/iris/x4/traits/tuple_traits.hpp @@ -72,8 +72,9 @@ struct unwrap_single_element_tuple_like template requires is_single_element_tuple_like_v struct unwrap_single_element_tuple_like - : unwrap_single_element_tuple_like> -{}; +{ + using type = alloy::tuple_element_t<0, T>; +}; } // 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..0296de0ef --- /dev/null +++ b/test/x4/single_element_tuple_like.cpp @@ -0,0 +1,42 @@ +#include "iris_x4_test.hpp" + +#include + +#include +#include + +struct Ident { + std::string value; +}; + +struct Var { + Ident ident; +}; + +IRIS_ALLOY_ADAPT_STRUCT(Ident, value) +IRIS_ALLOY_ADAPT_STRUCT(Var, ident) + +using IdentRule = x4::rule; +using VarRule = x4::rule; + +constexpr IdentRule ident; +constexpr VarRule var; + +IRIS_X4_DECLARE(IdentRule); +IRIS_X4_DECLARE(VarRule); + +constexpr auto ident_def = +x4::char_; +constexpr auto var_def = x4::lit('$') >> ident; + +IRIS_X4_DEFINE(ident); +IRIS_X4_DEFINE(var); + +TEST_CASE("single element tuple like") +{ + { + constexpr auto parser = x4::lit('$') >> ident; + Var attr; + CHECK(parse("$foo", parser, attr)); + CHECK(attr.ident.value == "foo"); + } +} From b634c28967dea7dc5cd280b0c05f677c7382fdba Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 20 Feb 2026 12:10:09 +0900 Subject: [PATCH 17/24] Restore noexcept --- include/iris/x4/core/move_to.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/iris/x4/core/move_to.hpp b/include/iris/x4/core/move_to.hpp index 7bf8b27b1..70d2fef84 100644 --- a/include/iris/x4/core/move_to.hpp +++ b/include/iris/x4/core/move_to.hpp @@ -251,8 +251,7 @@ template requires traits::is_single_element_tuple_like_v constexpr void move_to(Source&& src, Dest& dest) - // TODO: investigate wierd compilation error due to noexcept specification - // noexcept(noexcept(x4::move_to(std::forward(src), alloy::get<0>(dest)))) + noexcept(noexcept(x4::move_to(std::forward(src), alloy::get<0>(dest)))) { static_assert(!std::same_as, Dest>, "[BUG] This call should instead resolve to the overload handling identical types"); From b3b04787519be36ed26bdd94a3d718715dbd7411 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 20 Feb 2026 16:10:16 +0900 Subject: [PATCH 18/24] Add test --- test/x4/single_element_tuple_like.cpp | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/test/x4/single_element_tuple_like.cpp b/test/x4/single_element_tuple_like.cpp index 0296de0ef..40ccdc5da 100644 --- a/test/x4/single_element_tuple_like.cpp +++ b/test/x4/single_element_tuple_like.cpp @@ -6,37 +6,46 @@ #include struct Ident { - std::string value; + std::string value; }; struct Var { - Ident ident; + 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_; constexpr auto var_def = x4::lit('$') >> ident; +constexpr auto decl_def = x4::lit("let") >> var; IRIS_X4_DEFINE(ident); IRIS_X4_DEFINE(var); +IRIS_X4_DEFINE(decl); TEST_CASE("single element tuple like") { - { - constexpr auto parser = x4::lit('$') >> ident; - Var attr; - CHECK(parse("$foo", parser, attr)); - CHECK(attr.ident.value == "foo"); - } + { + Decl attr; + CHECK(parse("let$foo", decl, attr)); + CHECK(attr.var.ident.value == "foo"); + } } From f0d6ef5e327fbc1a239bbdad7abb95bc783faded Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 20 Feb 2026 16:14:18 +0900 Subject: [PATCH 19/24] Make test complex --- test/x4/single_element_tuple_like.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/x4/single_element_tuple_like.cpp b/test/x4/single_element_tuple_like.cpp index 40ccdc5da..1f459acb2 100644 --- a/test/x4/single_element_tuple_like.cpp +++ b/test/x4/single_element_tuple_like.cpp @@ -34,8 +34,8 @@ IRIS_X4_DECLARE(VarRule); IRIS_X4_DECLARE(DeclRule) constexpr auto ident_def = +x4::char_; -constexpr auto var_def = x4::lit('$') >> ident; -constexpr auto decl_def = x4::lit("let") >> var; +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); @@ -45,7 +45,7 @@ TEST_CASE("single element tuple like") { { Decl attr; - CHECK(parse("let$foo", decl, attr)); + CHECK(parse("let $foo", decl, attr)); CHECK(attr.var.ident.value == "foo"); } } From 52f30734caac87b7810361471f393917205409ac Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 20 Feb 2026 16:15:29 +0900 Subject: [PATCH 20/24] Make test more complex 2 --- test/x4/single_element_tuple_like.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/x4/single_element_tuple_like.cpp b/test/x4/single_element_tuple_like.cpp index 1f459acb2..64f34e9ba 100644 --- a/test/x4/single_element_tuple_like.cpp +++ b/test/x4/single_element_tuple_like.cpp @@ -33,7 +33,7 @@ IRIS_X4_DECLARE(IdentRule); IRIS_X4_DECLARE(VarRule); IRIS_X4_DECLARE(DeclRule) -constexpr auto ident_def = +x4::char_; +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]; From ea886bd3b2e76d6dc58a16ac16dc5a18de9641b5 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Tue, 24 Feb 2026 14:48:35 +0900 Subject: [PATCH 21/24] Fix --- include/iris/x4/core/detail/parse_into_container.hpp | 6 +++--- include/iris/x4/core/move_to.hpp | 4 ++-- include/iris/x4/rule.hpp | 2 +- include/iris/x4/traits/container_traits.hpp | 6 ------ 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/include/iris/x4/core/detail/parse_into_container.hpp b/include/iris/x4/core/detail/parse_into_container.hpp index f4c1a9463..a3b67b7e7 100644 --- a/include/iris/x4/core/detail/parse_into_container.hpp +++ b/include/iris/x4/core/detail/parse_into_container.hpp @@ -99,7 +99,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, x4::unwrap_single_element_tuple_like(attr)); + return parse_into_container_base_impl::call_synthesize(parser, first, last, ctx, attr); } // Parser has no attribute (pass unused) @@ -189,7 +189,7 @@ 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, @@ -210,7 +210,7 @@ parse_into_container( 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); + return parse_into_container_impl::call(parser, first, last, ctx, x4::unwrap_single_element_tuple_like(attr)); } } diff --git a/include/iris/x4/core/move_to.hpp b/include/iris/x4/core/move_to.hpp index 70d2fef84..cbef43b2e 100644 --- a/include/iris/x4/core/move_to.hpp +++ b/include/iris/x4/core/move_to.hpp @@ -128,7 +128,7 @@ move_to(Source&& src, Dest& dest) template Dest> requires - traits::is_same_size_tuple_like_v && + traits::is_same_size_tuple_like_v> && (!traits::is_single_element_tuple_like_v) constexpr void move_to(Source&& src, Dest& dest) @@ -143,7 +143,7 @@ move_to(Source&& src, Dest& dest) template Dest> requires -// traits::variant_has_substitute_v && // TODO: investigate compilation error due to existance of this constraint + traits::variant_has_substitute_v> && (!traits::is_single_element_tuple_like_v) constexpr void move_to(Source&& src, Dest& dest) diff --git a/include/iris/x4/rule.hpp b/include/iris/x4/rule.hpp index 02d9aa4e4..fb8ef1e3f 100644 --- a/include/iris/x4/rule.hpp +++ b/include/iris/x4/rule.hpp @@ -453,7 +453,7 @@ struct rule : parser> return true; } - // TODO: 良いコメントを書く + // rule's attribute type is the same as the exposed one template Se, class Context, X4Attribute Exposed> requires (!std::same_as, unused_type>) && diff --git a/include/iris/x4/traits/container_traits.hpp b/include/iris/x4/traits/container_traits.hpp index 3a19a29d1..157efc4a7 100644 --- a/include/iris/x4/traits/container_traits.hpp +++ b/include/iris/x4/traits/container_traits.hpp @@ -86,12 +86,6 @@ struct container_value template using container_value_t = typename container_value::type; -template - requires traits::is_single_element_tuple_like_v -struct container_value - : container_value> -{}; - template struct container_value : container_value {}; From 3556902a88a1e295f0e79e5e36415289f43e04a0 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Tue, 24 Feb 2026 15:18:23 +0900 Subject: [PATCH 22/24] Restore overloads --- include/iris/x4/core/move_to.hpp | 37 ++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/include/iris/x4/core/move_to.hpp b/include/iris/x4/core/move_to.hpp index cbef43b2e..ce8908640 100644 --- a/include/iris/x4/core/move_to.hpp +++ b/include/iris/x4/core/move_to.hpp @@ -141,10 +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::variant_has_substitute_v> && - (!traits::is_single_element_tuple_like_v) + (!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"); + 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_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) // TODO: add noexcept +{ + static_assert(!std::same_as, Dest>, "[BUG] This call should instead resolve to the overload handling identical types"); + + 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_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) @@ -250,8 +280,7 @@ move_to(It first, Se last, Dest& dest) template 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(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"); From f05137cbc35afd72e008045af0c7be8a0f7838ef Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Tue, 24 Feb 2026 16:34:02 +0900 Subject: [PATCH 23/24] Unwrap single element tuple on call site --- include/iris/x4/core/detail/parse_into_container.hpp | 6 +++--- include/iris/x4/core/detail/parse_sequence.hpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/iris/x4/core/detail/parse_into_container.hpp b/include/iris/x4/core/detail/parse_into_container.hpp index a3b67b7e7..22552ce16 100644 --- a/include/iris/x4/core/detail/parse_into_container.hpp +++ b/include/iris/x4/core/detail/parse_into_container.hpp @@ -99,7 +99,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); + 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) @@ -133,7 +133,7 @@ struct parse_into_container_impl It, Se, Context, typename parser_traits::attribute_type >::actual_type, - traits::container_value_t + traits::container_value_t> >> >; @@ -210,7 +210,7 @@ parse_into_container( 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, x4::unwrap_single_element_tuple_like(attr)); + 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 1b3cf3e2b..927e12ff1 100644 --- a/include/iris/x4/core/detail/parse_sequence.hpp +++ b/include/iris/x4/core/detail/parse_sequence.hpp @@ -268,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> From ada060c7b1dd1d778d4cadb50352f9788e7e43f1 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Tue, 24 Feb 2026 16:35:54 +0900 Subject: [PATCH 24/24] Move unwrapping helper function to tuple_traits.hpp --- .../x4/core/detail/parse_into_container.hpp | 1 - .../core/unwrap_single_element_tuple_like.hpp | 25 ------------------- include/iris/x4/traits/tuple_traits.hpp | 21 ++++++++++++++-- 3 files changed, 19 insertions(+), 28 deletions(-) delete mode 100644 include/iris/x4/core/unwrap_single_element_tuple_like.hpp diff --git a/include/iris/x4/core/detail/parse_into_container.hpp b/include/iris/x4/core/detail/parse_into_container.hpp index 22552ce16..d36ecb322 100644 --- a/include/iris/x4/core/detail/parse_into_container.hpp +++ b/include/iris/x4/core/detail/parse_into_container.hpp @@ -14,7 +14,6 @@ #include #include -#include #include #include diff --git a/include/iris/x4/core/unwrap_single_element_tuple_like.hpp b/include/iris/x4/core/unwrap_single_element_tuple_like.hpp deleted file mode 100644 index 0f186bf40..000000000 --- a/include/iris/x4/core/unwrap_single_element_tuple_like.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef IRIS_X4_CORE_UNWRAP_SINGLE_ELEMENT_TUPLE_LIKE_HPP -#define IRIS_X4_CORE_UNWRAP_SINGLE_ELEMENT_TUPLE_LIKE_HPP - -#include - -#include - -namespace iris::x4 { - -template -constexpr T&& unwrap_single_element_tuple_like(T&& t) noexcept -{ - 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)); -} - -} - -#endif diff --git a/include/iris/x4/traits/tuple_traits.hpp b/include/iris/x4/traits/tuple_traits.hpp index 8b6291258..f481d89c2 100644 --- a/include/iris/x4/traits/tuple_traits.hpp +++ b/include/iris/x4/traits/tuple_traits.hpp @@ -14,7 +14,9 @@ #include -namespace iris::x4::traits { +namespace iris::x4 { + +namespace traits { template struct is_same_size_tuple_like_impl {}; @@ -76,6 +78,21 @@ struct unwrap_single_element_tuple_like using type = alloy::tuple_element_t<0, T>; }; -} // iris::x4::traits +} // triats + +template +constexpr T&& unwrap_single_element_tuple_like(T&& t) noexcept +{ + 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 #endif