From 270fe8f854d1fdecffd0d2dfaefa93f97103c64c Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Fri, 25 Sep 2026 04:02:07 +0900 Subject: [PATCH 1/7] Add test --- test/x4/rule1.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/x4/rule1.cpp b/test/x4/rule1.cpp index dcd73998c..25edbfd8b 100644 --- a/test/x4/rule1.cpp +++ b/test/x4/rule1.cpp @@ -17,6 +17,11 @@ #include #include +#include + +#include +#include + TEST_CASE("rule: constructible with incomplete type") { struct incomplete_type; @@ -45,6 +50,13 @@ TEST_CASE("rule1") static_assert( x4::has_attribute_v{} = int_)>); static_assert(!x4::has_attribute_v{} = int_)>); + STATIC_CHECK( x4::parser_traits>::handles_container); + STATIC_CHECK( x4::parser_traits>>::handles_container>); + //STATIC_CHECK( x4::parser_traits>>::handles_container>>); + STATIC_CHECK(!x4::parser_traits>::handles_container>); // yields an element, not the container + //STATIC_CHECK(!x4::parser_traits>>::handles_container); // yields a variant, cannot write into `std::string` + STATIC_CHECK(!x4::parser_traits>>::handles_container>>); + { // basic tests From dd0596cf7886e390a76ec284c197082ce4f48a0c Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Fri, 25 Sep 2026 19:00:13 +0900 Subject: [PATCH 2/7] Update iris/alloy --- modules/iris | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/iris b/modules/iris index e3df15f5b..6917f1f78 160000 --- a/modules/iris +++ b/modules/iris @@ -1 +1 @@ -Subproject commit e3df15f5bead20bc45e4a0c59aa09209abb73638 +Subproject commit 6917f1f78dff6ef5f85a12b533418be7b3297ffb From 364e20f6d8dd0a22479ec3d49d8ef0df8b317e12 Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Fri, 25 Sep 2026 19:37:02 +0900 Subject: [PATCH 3/7] Fix narrowing conversion in numeric tests --- test/x4/real.hpp | 11 ++++++++--- test/x4/uint.cpp | 7 ++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/test/x4/real.hpp b/test/x4/real.hpp index cf0cc7128..052b9c968 100644 --- a/test/x4/real.hpp +++ b/test/x4/real.hpp @@ -60,8 +60,12 @@ struct ts_real_policies : x4::ureal_policies constexpr uint_parser uint3; constexpr uint_parser uint3_3; - if (auto res = parse(first, last, uint3, result); res.ok) { - Accumulator n; + // `unsigned -> Accumulator` (a floating point type) is a narrowing + // conversion, so the digits are parsed into `unsigned` and converted + // explicitly. + unsigned digits = 0; + if (auto res = parse(first, last, uint3, digits); res.ok) { + result = static_cast(digits); It iter = res.remainder.begin(); first = iter; @@ -70,11 +74,12 @@ struct ts_real_policies : x4::ureal_policies if (!res.ok) break; iter = res.remainder.begin(); + unsigned n = 0; parse(res, iter, last, uint3_3, n); if (!res.ok) break; iter = res.remainder.begin(); - result = result * 1000 + n; + result = result * 1000 + static_cast(n); first = iter; } diff --git a/test/x4/uint.cpp b/test/x4/uint.cpp index 729ce1cac..52e39eaf6 100644 --- a/test/x4/uint.cpp +++ b/test/x4/uint.cpp @@ -236,8 +236,9 @@ TEST_CASE("uint") CHECK(parse("4294967295", uint32_, u32)); } { + // a signed `T`; the attribute must be `T` too, `std::int8_t -> std::uint8_t` is narrowing x4::uint_parser u_int8_; - std::uint8_t u8 = 0; + std::int8_t u8 = 0; CHECK(!parse("999", u_int8_, u8)); CHECK(!parse("-1", u_int8_, u8)); @@ -247,7 +248,7 @@ TEST_CASE("uint") } { x4::uint_parser u_int16_; - std::uint16_t u16 = 0; + std::int16_t u16 = 0; CHECK(!parse("99999", u_int16_, u16)); CHECK(!parse("-1", u_int16_, u16)); @@ -257,7 +258,7 @@ TEST_CASE("uint") } { x4::uint_parser u_int32_; - std::uint32_t u32 = 0; + std::int32_t u32 = 0; CHECK(!parse("9999999999", u_int32_, u32)); CHECK(!parse("-1", u_int32_, u32)); From 98e5c422352f9e2fc37bbd3c841d6fc594ddc328 Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Fri, 25 Sep 2026 21:23:01 +0900 Subject: [PATCH 4/7] Make single-element tuple-like naming consistent --- .../iris/x4/core/detail/parse_alternative.hpp | 4 +- .../x4/core/detail/parse_into_container.hpp | 8 +- .../iris/x4/core/detail/parse_sequence.hpp | 2 +- include/iris/x4/core/list_like_parser.hpp | 36 +----- include/iris/x4/core/move_to.hpp | 16 +-- include/iris/x4/core/traits/can_hold.hpp | 2 +- include/iris/x4/core/traits/tuple_traits.hpp | 85 ++++++++----- .../iris/x4/string/detail/string_parse.hpp | 10 -- test/x4/CMakeLists.txt | 1 + test/x4/alternative.cpp | 4 +- test/x4/int.cpp | 2 +- test/x4/lit.cpp | 2 +- test/x4/omit.cpp | 2 +- test/x4/plus.cpp | 2 +- test/x4/rule4.cpp | 2 +- test/x4/sequence.cpp | 12 +- test/x4/tuple_like.cpp | 113 ++++++++++++++++++ 17 files changed, 205 insertions(+), 98 deletions(-) create mode 100644 test/x4/tuple_like.cpp diff --git a/include/iris/x4/core/detail/parse_alternative.hpp b/include/iris/x4/core/detail/parse_alternative.hpp index 25167d704..4217330d7 100644 --- a/include/iris/x4/core/detail/parse_alternative.hpp +++ b/include/iris/x4/core/detail/parse_alternative.hpp @@ -110,9 +110,9 @@ struct pass_non_variant_attribute } }; -// Unwrap single element sequences +// Unwrap single-element tuple-likes template - requires tuple_is_size_one_sequence_v + requires tuple_is_single_element_v struct pass_non_variant_attribute { using attr_type = std::remove_reference_t< diff --git a/include/iris/x4/core/detail/parse_into_container.hpp b/include/iris/x4/core/detail/parse_into_container.hpp index 64fd2a440..2f4e07792 100644 --- a/include/iris/x4/core/detail/parse_into_container.hpp +++ b/include/iris/x4/core/detail/parse_into_container.hpp @@ -83,11 +83,11 @@ struct parse_into_container_impl_default } } else { - if constexpr (tuple_is_size_one_sequence_v) { - // attribute is single element tuple-like; unwrap and try again + if constexpr (tuple_is_single_element_v) { + // attribute is a single-element tuple-like; unwrap and try again return parse_into_container_impl_default::call(parser, first, last, ctx, alloy::get<0>(unwrapped_attr)); } else { - static_assert(false, "[BUG] parse_into_container accepts a container, a variant of container or a single element tuple-like of container"); + static_assert(false, "[BUG] parse_into_container accepts a container, a variant of container or a single-element tuple-like of container"); return false; } } @@ -110,7 +110,7 @@ parse_into_container(Parser const& parser, It& first, Se const& last, Context co } else if constexpr (is_recursive_wrapper_v) { return detail::parse_into_container(parser, first, last, ctx, *attr); - } else if constexpr (tuple_is_size_one_sequence_v) { + } else if constexpr (tuple_is_single_element_v) { // A tuple-like holding a single container; parse into that container return detail::parse_into_container(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 4e6acc524..5b40b9e26 100644 --- a/include/iris/x4/core/detail/parse_sequence.hpp +++ b/include/iris/x4/core/detail/parse_sequence.hpp @@ -111,7 +111,7 @@ struct parse_sequence_tuple if constexpr (I != layout::single_attributed_index) { return elem.parse(first, last, ctx, unused); - } else if constexpr (tuple_is_size_one_view_v && !sequence_passes_view::value) { + } else if constexpr (tuple_is_single_element_view_v && !sequence_passes_view::value) { auto&& elem_attr = x4::make_container_appender(alloy::get<0>(attr)); return elem.parse(first, last, ctx, elem_attr); diff --git a/include/iris/x4/core/list_like_parser.hpp b/include/iris/x4/core/list_like_parser.hpp index 0d712b463..1294834bd 100644 --- a/include/iris/x4/core/list_like_parser.hpp +++ b/include/iris/x4/core/list_like_parser.hpp @@ -29,11 +29,11 @@ template // non-variant `ExposedAttr` struct unwrap_container_candidate { - using type = synthesized_value< + using type = unwrap_single_element_t< unwrap_recursive_t< typename unwrap_container_appender::type > - >::type; + >; }; template @@ -52,32 +52,6 @@ struct chunk_buffer_impl static_assert(traits::X4Container::type>); }; -template -[[nodiscard]] constexpr auto&& unwrap_single_element(T&& value) noexcept -{ - return std::forward(value); -} - -template - requires tuple_is_size_one_sequence_v> -[[nodiscard]] constexpr auto&& unwrap_single_element(T&& value) noexcept -{ - return std::forward_like(alloy::get<0>(std::forward(value))); -} - -template -struct unwrap_single_element_plain -{ - using type = std::remove_cvref_t; -}; - -template - requires tuple_is_size_one_sequence_v> -struct unwrap_single_element_plain -{ - using type = std::remove_cvref_t>; -}; - } // detail @@ -88,10 +62,8 @@ using chunk_buffer = detail::chunk_buffer_impl::type; template [[nodiscard]] constexpr auto& get_container(ExposedAttr& attr) { - using unwrapped_attr_type = detail::unwrap_single_element_plain< - unwrap_recursive_t - >::type; - auto& unwrapped_attr = detail::unwrap_single_element(iris::unwrap_recursive(attr)); + using unwrapped_attr_type = unwrap_single_element_t>; + auto& unwrapped_attr = x4::unwrap_single_element(iris::unwrap_recursive(attr)); if constexpr (is_variant_v) { using container_alternative = variant_find_holdable_type< diff --git a/include/iris/x4/core/move_to.hpp b/include/iris/x4/core/move_to.hpp index b44ab01a0..6a0810385 100644 --- a/include/iris/x4/core/move_to.hpp +++ b/include/iris/x4/core/move_to.hpp @@ -115,7 +115,7 @@ constexpr void move_to(It const&, Se const&, unused_type const&&) = delete; // t // Category specific -------------------------------------- template Dest> - requires tuple_is_size_one_sequence_v + requires tuple_is_single_element_v constexpr void move_to(Source&& src, Dest& dest) noexcept(noexcept(dest = std::forward_like(alloy::get<0>(std::forward(src))))) @@ -126,7 +126,7 @@ move_to(Source&& src, Dest& dest) } template Dest> - requires (!tuple_is_size_one_sequence_v) + requires (!tuple_is_single_element_v) constexpr void move_to(Source&& src, Dest& dest) noexcept(std::is_nothrow_assignable_v) @@ -140,8 +140,8 @@ move_to(Source&& src, Dest& dest) template Dest> requires - tuple_is_same_size_sequence_v && - (!tuple_is_size_one_sequence_v) + tuple_is_same_size_v && + (!tuple_is_single_element_v) constexpr void move_to(Source&& src, Dest& dest) noexcept(noexcept(alloy::tuple_assign(std::forward(src), dest))) @@ -167,7 +167,7 @@ move_to(Source&& src, Dest& dest) } template Dest> - requires (!std::is_assignable_v) && tuple_is_size_one_sequence_v + requires (!std::is_assignable_v) && tuple_is_single_element_v constexpr void move_to(Source&& src, Dest& dest) noexcept(noexcept(dest = std::forward_like(alloy::get<0>(std::forward(src))))) @@ -229,7 +229,7 @@ move_to(It first, Se last, Dest& dest) } template Se, CategorizedAttr Dest> - requires tuple_is_size_one_sequence_v + requires tuple_is_single_element_v constexpr void move_to(It first, Se last, Dest& dest) noexcept(noexcept(x4::move_to(first, last, alloy::get<0>(dest)))) @@ -274,9 +274,9 @@ move_to(Source&& src, Dest& dest) } } -// Size-one tuple-like forwarding +// Single-element tuple-like forwarding template Dest> - requires tuple_is_size_one_sequence_v + requires tuple_is_single_element_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/core/traits/can_hold.hpp b/include/iris/x4/core/traits/can_hold.hpp index 25425d699..fb9a1481e 100644 --- a/include/iris/x4/core/traits/can_hold.hpp +++ b/include/iris/x4/core/traits/can_hold.hpp @@ -43,7 +43,7 @@ template struct is_all_substitute_for_tuple : std::false_type {}; template - requires tuple_is_same_size_sequence_v + requires tuple_is_same_size_v struct is_all_substitute_for_tuple : is_all_substitute_for_tuple_impl {}; template diff --git a/include/iris/x4/core/traits/tuple_traits.hpp b/include/iris/x4/core/traits/tuple_traits.hpp index 124f58a91..e544ec501 100644 --- a/include/iris/x4/core/traits/tuple_traits.hpp +++ b/include/iris/x4/core/traits/tuple_traits.hpp @@ -15,9 +15,14 @@ #include #include +#include + +#include namespace iris::x4 { +namespace detail { + template struct tuple_has_same_size : std::bool_constant< @@ -26,68 +31,94 @@ struct tuple_has_same_size > {}; -template -constexpr bool tuple_has_same_size_v = tuple_has_same_size::value; - template struct tuple_has_size : std::bool_constant> == N> {}; -template -constexpr bool tuple_has_size_v = tuple_has_size::value; +} // detail template -struct tuple_is_same_size_sequence +struct tuple_is_same_size : std::bool_constant>, alloy::is_tuple_like>, - tuple_has_same_size + detail::tuple_has_same_size >> {}; template -constexpr bool tuple_is_same_size_sequence_v = tuple_is_same_size_sequence::value; +constexpr bool tuple_is_same_size_v = tuple_is_same_size::value; -template -struct tuple_is_size_one_sequence +// A single-element tuple-like: `alloy::tuple`, or a user-defined struct +// adapted with a single member +template +struct tuple_is_single_element : std::bool_constant>, - tuple_has_size + alloy::is_tuple_like>, + detail::tuple_has_size >> {}; -template -constexpr bool tuple_is_size_one_sequence_v = tuple_is_size_one_sequence::value; +template +constexpr bool tuple_is_single_element_v = tuple_is_single_element::value; -template -struct tuple_is_size_one_view +// A single-element tuple-like whose element is an lvalue reference +template +struct tuple_is_single_element_view : std::bool_constant>, - tuple_has_size + alloy::is_tuple_like_view>, + detail::tuple_has_size >> {}; -template -constexpr bool tuple_is_size_one_view_v = tuple_is_size_one_view::value; +template +constexpr bool tuple_is_single_element_view_v = tuple_is_single_element_view::value; +namespace detail { + template -struct synthesized_value +struct unwrap_single_element_impl { - using type = T; + using type = std::remove_cvref_t; }; template -using synthesized_value_t = typename synthesized_value::type; + requires tuple_is_single_element_v +struct unwrap_single_element_impl +{ + using type = std::remove_cvref_t>>; +}; -template - requires tuple_is_size_one_sequence_v> -struct synthesized_value +struct unwrap_single_element_fn { - using type = std::remove_cvref_t>; + template + [[nodiscard]] static constexpr T&& operator()(T&& value) noexcept + { + return std::forward(value); + } + + template + requires tuple_is_single_element_v + [[nodiscard]] static constexpr decltype(auto) operator()(T&& value) noexcept + { + return alloy::get<0>(std::forward(value)); + } }; +} // detail + +// The element of a single-element tuple-like, or the type itself otherwise; +// cv-qualifiers and references are removed. One level only. +template +using unwrap_single_element_t = detail::unwrap_single_element_impl::type; + +// The element of a single-element tuple-like, exactly as `alloy::get<0>` +// yields it (a reference element keeps its own reference type), or the +// argument itself otherwise +inline constexpr detail::unwrap_single_element_fn unwrap_single_element{}; + } // iris::x4 #endif diff --git a/include/iris/x4/string/detail/string_parse.hpp b/include/iris/x4/string/detail/string_parse.hpp index 26498888a..ed02f408a 100644 --- a/include/iris/x4/string/detail/string_parse.hpp +++ b/include/iris/x4/string/detail/string_parse.hpp @@ -10,9 +10,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ==============================================================================*/ -#include #include -#include #include @@ -36,10 +34,6 @@ string_parse( static_assert(!std::is_array_v); using CharT = std::ranges::range_value_t; - using synthesized_value_type = synthesized_value_t; - static_assert(std::same_as, container_tag>); - using value_type = traits::container_value::type; - static_assert(!CharLike || !CharIncompatibleWith, "Mixing incompatible char types is not allowed"); static_assert(!CharIncompatibleWith, CharT>, "Mixing incompatible char types is not allowed"); It it = first; @@ -84,10 +78,6 @@ string_parse( It& first, Se const& last, Attr& attr ) noexcept(std::same_as, unused_container_type>) { - using synthesized_value_type = synthesized_value_t; - static_assert(std::same_as, container_tag>); - using value_type = traits::container_value::type; - static_assert(!CharLike || !CharIncompatibleWith, "Mixing incompatible char types is not allowed"); static_assert(!CharIncompatibleWith, CharT>, "Mixing incompatible char types is not allowed"); auto uc_it = ucstr.begin(); diff --git a/test/x4/CMakeLists.txt b/test/x4/CMakeLists.txt index b8f38d80a..31d8f5262 100644 --- a/test/x4/CMakeLists.txt +++ b/test/x4/CMakeLists.txt @@ -87,6 +87,7 @@ x4_define_tests( symbols2 symbols3 tst + tuple_like uint uint_radix unused diff --git a/test/x4/alternative.cpp b/test/x4/alternative.cpp index e9132fea7..600373ebe 100644 --- a/test/x4/alternative.cpp +++ b/test/x4/alternative.cpp @@ -277,7 +277,7 @@ TEST_CASE("alternative") REQUIRE(parse("123", parser, var)); } - // single element tuple-like case + // single-element tuple-like case { alloy::tuple> fv; REQUIRE(parse("12345", int_ | +char_, fv)); @@ -289,7 +289,7 @@ TEST_CASE("alternative") CHECK(iris::get(alloy::get<0>(fvi)) == 12345); } - // alternative over single element tuple as part of another tuple + // alternative over a single-element tuple-like as part of another tuple { constexpr auto key1 = lit("long") >> fixed_value(long{}); constexpr auto key2 = lit("char") >> fixed_value(char{}); diff --git a/test/x4/int.cpp b/test/x4/int.cpp index 369f45efb..aa9629888 100644 --- a/test/x4/int.cpp +++ b/test/x4/int.cpp @@ -231,7 +231,7 @@ TEST_CASE("int") CHECK(parse("-12", int2, i)); } - // single-element tuple tests + // single-element tuple-like tests { alloy::tuple i{}; diff --git a/test/x4/lit.cpp b/test/x4/lit.cpp index 743ef7901..03cece7f3 100644 --- a/test/x4/lit.cpp +++ b/test/x4/lit.cpp @@ -132,7 +132,7 @@ TEST_CASE("lit") } { - // single-element tuple tests + // single-element tuple-like tests alloy::tuple s; REQUIRE(parse("kimpo", x4::string("kimpo"), s)); CHECK(alloy::get<0>(s) == "kimpo"); diff --git a/test/x4/omit.cpp b/test/x4/omit.cpp index 6fb9181ad..c7aaf310d 100644 --- a/test/x4/omit.cpp +++ b/test/x4/omit.cpp @@ -56,7 +56,7 @@ TEST_CASE("omit") { // If all elements except 1 is omitted, the attribute is - // a single-element tuple. For this case alone, we allow + // a single-element tuple-like. For this case alone, we allow // naked attributes. char attr{}; REQUIRE(parse("abc", omit[char_] >> 'b' >> char_, attr)); diff --git a/test/x4/plus.cpp b/test/x4/plus.cpp index e0f950fd7..8b792f198 100644 --- a/test/x4/plus.cpp +++ b/test/x4/plus.cpp @@ -112,7 +112,7 @@ TEST_CASE("plus") (void)parse("abcde", +char_, x); } - // single-element tuple tests + // single-element tuple-like tests { alloy::tuple fs; REQUIRE(parse("12345", +char_, fs)); diff --git a/test/x4/rule4.cpp b/test/x4/rule4.cpp index 4314ef3e7..aa434cdae 100644 --- a/test/x4/rule4.cpp +++ b/test/x4/rule4.cpp @@ -169,7 +169,7 @@ TEST_CASE("rule4") CHECK(*ov == 1); } - // test handling of single element tuple + // test handling of a single-element tuple-like { auto r = rule>{} = int_; diff --git a/test/x4/sequence.cpp b/test/x4/sequence.cpp index 656afde3d..92e6dbf66 100644 --- a/test/x4/sequence.cpp +++ b/test/x4/sequence.cpp @@ -124,19 +124,19 @@ TEST_CASE("sequence") } { - // a single element tuple + // a single-element tuple-like std::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: + // Make sure single-element tuple-likes get passed through if the rhs + // has a single-element tuple-like 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. + // single-element tuple-like, so the original comment is not accurate. // rule version { @@ -162,8 +162,8 @@ TEST_CASE("sequence") } { - // make sure single element tuples get passed through if the rhs - // has a single element tuple as its attribute. This is a correction + // make sure single-element tuple-likes get passed through if the rhs + // has a single-element tuple-like as its attribute. This is a correction // of the test above. // rule version diff --git a/test/x4/tuple_like.cpp b/test/x4/tuple_like.cpp new file mode 100644 index 000000000..6eeb59215 --- /dev/null +++ b/test/x4/tuple_like.cpp @@ -0,0 +1,113 @@ +#include "iris_x4_test.hpp" + +#include + +#include +#include + +#include +#include + +namespace { + +struct adapted_int +{ + int value = 0; +}; + +template +struct fixed_ref +{ + int* target = nullptr; +}; + +template +struct fixed_ref_getter +{ + template + constexpr Ref operator()(Self&& self) const noexcept + { + return static_cast(*self.target); + } +}; + +template +using unwrapped_t = decltype(x4::unwrap_single_element(std::declval())); + +} // anonymous + +IRIS_ALLOY_ADAPT_STRUCT(adapted_int, value); + +template +struct iris::alloy::adaptor> +{ + using getters_list = iris::constant_list{}>; +}; + +TEST_CASE("unwrap_single_element") +{ + using alloy::tuple; + + STATIC_CHECK(std::same_as&>, int&>); + STATIC_CHECK(std::same_as const&>, int const&>); + STATIC_CHECK(std::same_as&&>, int&&>); + STATIC_CHECK(std::same_as const&&>, int const&&>); + + STATIC_CHECK(std::same_as, int&>); + STATIC_CHECK(std::same_as, int const&>); + STATIC_CHECK(std::same_as, int&&>); + STATIC_CHECK(std::same_as, int const&&>); + + STATIC_CHECK(std::same_as&>, int&>); + STATIC_CHECK(std::same_as const&>, int&>); + STATIC_CHECK(std::same_as&&>, int&>); + STATIC_CHECK(std::same_as const&&>, int&>); + + STATIC_CHECK(std::same_as&>, int const&>); + STATIC_CHECK(std::same_as const&>, int const&>); + STATIC_CHECK(std::same_as&&>, int const&>); + STATIC_CHECK(std::same_as const&&>, int const&>); + + STATIC_CHECK(std::same_as&>, int&>); + STATIC_CHECK(std::same_as const&>, int&>); + STATIC_CHECK(std::same_as&&>, int&>); + STATIC_CHECK(std::same_as const&&>, int&>); + + STATIC_CHECK(std::same_as&>, int const&>); + STATIC_CHECK(std::same_as const&>, int const&>); + STATIC_CHECK(std::same_as&&>, int const&>); + STATIC_CHECK(std::same_as const&&>, int const&>); + + STATIC_CHECK(std::same_as&>, int&&>); + STATIC_CHECK(std::same_as const&>, int&&>); + STATIC_CHECK(std::same_as&&>, int&&>); + STATIC_CHECK(std::same_as const&&>, int&&>); + + STATIC_CHECK(std::same_as&>, int const&&>); + STATIC_CHECK(std::same_as const&>, int const&&>); + STATIC_CHECK(std::same_as&&>, int const&&>); + STATIC_CHECK(std::same_as const&&>, int const&&>); + + STATIC_CHECK(std::same_as, int&>); + STATIC_CHECK(std::same_as, int const&>); + STATIC_CHECK(std::same_as, int&&>); + STATIC_CHECK(std::same_as, int const&&>); + STATIC_CHECK(std::same_as&>, tuple&>); + + STATIC_CHECK(std::same_as const&>, int>); + STATIC_CHECK(std::same_as, int>); + STATIC_CHECK(std::same_as>, int>); + STATIC_CHECK(std::same_as>>, tuple>); + STATIC_CHECK(std::same_as, int>); + + { + tuple t{42}; + CHECK(&x4::unwrap_single_element(t) == &alloy::get<0>(t)); + + int n = 42; + tuple const ref_tuple{n}; + CHECK(&x4::unwrap_single_element(ref_tuple) == &n); + CHECK(&x4::unwrap_single_element(fixed_ref{&n}) == &n); + CHECK(&x4::unwrap_single_element(n) == &n); + } +} From 0943f6b734372e17ce42042a2d715fd75614437d Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Fri, 25 Sep 2026 21:53:21 +0900 Subject: [PATCH 5/7] Reduce usage of conjunction/disjunction, use more concepts --- include/iris/x4/allocator.hpp | 12 ++-- .../iris/x4/core/detail/parse_alternative.hpp | 2 +- .../x4/core/detail/parse_into_container.hpp | 4 +- .../iris/x4/core/detail/parse_sequence.hpp | 2 +- include/iris/x4/core/move_to.hpp | 14 ++--- include/iris/x4/core/traits/can_hold.hpp | 2 +- include/iris/x4/core/traits/tuple_traits.hpp | 63 ++++--------------- include/iris/x4/debug/print_attribute.hpp | 8 +-- include/iris/x4/directive/repeat.hpp | 7 +-- include/iris/x4/operator/delimited_list.hpp | 7 +-- include/iris/x4/operator/kleene.hpp | 7 +-- include/iris/x4/operator/optional.hpp | 7 +-- include/iris/x4/operator/plus.hpp | 7 +-- test/x4/tuple_like.cpp | 21 +++++++ 14 files changed, 68 insertions(+), 95 deletions(-) diff --git a/include/iris/x4/allocator.hpp b/include/iris/x4/allocator.hpp index 6b3100309..4e6f3129d 100644 --- a/include/iris/x4/allocator.hpp +++ b/include/iris/x4/allocator.hpp @@ -23,12 +23,12 @@ template struct allocator_ops { template - static constexpr bool move_assign_noexcept = std::conjunction_v< - std::disjunction< - typename std::allocator_traits::propagate_on_container_move_assignment, - typename std::allocator_traits::is_always_equal - >... - >; + static constexpr bool move_assign_noexcept = ( + ( + std::allocator_traits::propagate_on_container_move_assignment::value || + std::allocator_traits::is_always_equal::value + ) && ... + ); template [[nodiscard]] static constexpr auto copy_construct(Klass& self, Klass const& other) diff --git a/include/iris/x4/core/detail/parse_alternative.hpp b/include/iris/x4/core/detail/parse_alternative.hpp index 4217330d7..320b6b553 100644 --- a/include/iris/x4/core/detail/parse_alternative.hpp +++ b/include/iris/x4/core/detail/parse_alternative.hpp @@ -112,7 +112,7 @@ struct pass_non_variant_attribute // Unwrap single-element tuple-likes template - requires tuple_is_single_element_v + requires SingleElementTupleLike struct pass_non_variant_attribute { using attr_type = std::remove_reference_t< diff --git a/include/iris/x4/core/detail/parse_into_container.hpp b/include/iris/x4/core/detail/parse_into_container.hpp index 2f4e07792..1f3ca1798 100644 --- a/include/iris/x4/core/detail/parse_into_container.hpp +++ b/include/iris/x4/core/detail/parse_into_container.hpp @@ -83,7 +83,7 @@ struct parse_into_container_impl_default } } else { - if constexpr (tuple_is_single_element_v) { + if constexpr (SingleElementTupleLike) { // attribute is a single-element tuple-like; unwrap and try again return parse_into_container_impl_default::call(parser, first, last, ctx, alloy::get<0>(unwrapped_attr)); } else { @@ -110,7 +110,7 @@ parse_into_container(Parser const& parser, It& first, Se const& last, Context co } else if constexpr (is_recursive_wrapper_v) { return detail::parse_into_container(parser, first, last, ctx, *attr); - } else if constexpr (tuple_is_single_element_v) { + } else if constexpr (SingleElementTupleLike) { // A tuple-like holding a single container; parse into that container return detail::parse_into_container(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 5b40b9e26..7f7b571ad 100644 --- a/include/iris/x4/core/detail/parse_sequence.hpp +++ b/include/iris/x4/core/detail/parse_sequence.hpp @@ -111,7 +111,7 @@ struct parse_sequence_tuple if constexpr (I != layout::single_attributed_index) { return elem.parse(first, last, ctx, unused); - } else if constexpr (tuple_is_single_element_view_v && !sequence_passes_view::value) { + } else if constexpr (SingleElementTupleLikeView && !sequence_passes_view::value) { auto&& elem_attr = x4::make_container_appender(alloy::get<0>(attr)); return elem.parse(first, last, ctx, elem_attr); diff --git a/include/iris/x4/core/move_to.hpp b/include/iris/x4/core/move_to.hpp index 6a0810385..44bef9a55 100644 --- a/include/iris/x4/core/move_to.hpp +++ b/include/iris/x4/core/move_to.hpp @@ -115,7 +115,7 @@ constexpr void move_to(It const&, Se const&, unused_type const&&) = delete; // t // Category specific -------------------------------------- template Dest> - requires tuple_is_single_element_v + requires SingleElementTupleLike constexpr void move_to(Source&& src, Dest& dest) noexcept(noexcept(dest = std::forward_like(alloy::get<0>(std::forward(src))))) @@ -126,7 +126,7 @@ move_to(Source&& src, Dest& dest) } template Dest> - requires (!tuple_is_single_element_v) + requires (!SingleElementTupleLike) constexpr void move_to(Source&& src, Dest& dest) noexcept(std::is_nothrow_assignable_v) @@ -140,8 +140,8 @@ move_to(Source&& src, Dest& dest) template Dest> requires - tuple_is_same_size_v && - (!tuple_is_single_element_v) + SameSizeTupleLike && + (!SingleElementTupleLike) constexpr void move_to(Source&& src, Dest& dest) noexcept(noexcept(alloy::tuple_assign(std::forward(src), dest))) @@ -167,7 +167,7 @@ move_to(Source&& src, Dest& dest) } template Dest> - requires (!std::is_assignable_v) && tuple_is_single_element_v + requires (!std::is_assignable_v) && SingleElementTupleLike constexpr void move_to(Source&& src, Dest& dest) noexcept(noexcept(dest = std::forward_like(alloy::get<0>(std::forward(src))))) @@ -229,7 +229,7 @@ move_to(It first, Se last, Dest& dest) } template Se, CategorizedAttr Dest> - requires tuple_is_single_element_v + requires SingleElementTupleLike constexpr void move_to(It first, Se last, Dest& dest) noexcept(noexcept(x4::move_to(first, last, alloy::get<0>(dest)))) @@ -276,7 +276,7 @@ move_to(Source&& src, Dest& dest) // Single-element tuple-like forwarding template Dest> - requires tuple_is_single_element_v + requires SingleElementTupleLike 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/core/traits/can_hold.hpp b/include/iris/x4/core/traits/can_hold.hpp index fb9a1481e..1e763a5f9 100644 --- a/include/iris/x4/core/traits/can_hold.hpp +++ b/include/iris/x4/core/traits/can_hold.hpp @@ -43,7 +43,7 @@ template struct is_all_substitute_for_tuple : std::false_type {}; template - requires tuple_is_same_size_v + requires SameSizeTupleLike struct is_all_substitute_for_tuple : is_all_substitute_for_tuple_impl {}; template diff --git a/include/iris/x4/core/traits/tuple_traits.hpp b/include/iris/x4/core/traits/tuple_traits.hpp index e544ec501..95c8793d7 100644 --- a/include/iris/x4/core/traits/tuple_traits.hpp +++ b/include/iris/x4/core/traits/tuple_traits.hpp @@ -17,64 +17,25 @@ #include #include -#include - namespace iris::x4 { -namespace detail { - -template -struct tuple_has_same_size - : std::bool_constant< - alloy::tuple_size_v> == - alloy::tuple_size_v> - > -{}; - -template -struct tuple_has_size - : std::bool_constant> == N> -{}; - -} // detail - -template -struct tuple_is_same_size - : std::bool_constant>, - alloy::is_tuple_like>, - detail::tuple_has_same_size - >> -{}; - +// Tuple-likes of the same size template -constexpr bool tuple_is_same_size_v = tuple_is_same_size::value; +concept SameSizeTupleLike = + alloy::TupleLike && + alloy::TupleLike && + (alloy::tuple_size_v> == alloy::tuple_size_v>); // A single-element tuple-like: `alloy::tuple`, or a user-defined struct // adapted with a single member template -struct tuple_is_single_element - : std::bool_constant>, - detail::tuple_has_size - >> -{}; - -template -constexpr bool tuple_is_single_element_v = tuple_is_single_element::value; +concept SingleElementTupleLike = + alloy::TupleLike && + (alloy::tuple_size_v> == 1); // A single-element tuple-like whose element is an lvalue reference template -struct tuple_is_single_element_view - : std::bool_constant>, - detail::tuple_has_size - >> -{}; - -template -constexpr bool tuple_is_single_element_view_v = tuple_is_single_element_view::value; - +concept SingleElementTupleLikeView = SingleElementTupleLike && alloy::TupleLikeView; namespace detail { @@ -84,8 +45,7 @@ struct unwrap_single_element_impl using type = std::remove_cvref_t; }; -template - requires tuple_is_single_element_v +template struct unwrap_single_element_impl { using type = std::remove_cvref_t>>; @@ -99,8 +59,7 @@ struct unwrap_single_element_fn return std::forward(value); } - template - requires tuple_is_single_element_v + template [[nodiscard]] static constexpr decltype(auto) operator()(T&& value) noexcept { return alloy::get<0>(std::forward(value)); diff --git a/include/iris/x4/debug/print_attribute.hpp b/include/iris/x4/debug/print_attribute.hpp index 5082bc60d..29364c23a 100644 --- a/include/iris/x4/debug/print_attribute.hpp +++ b/include/iris/x4/debug/print_attribute.hpp @@ -236,11 +236,9 @@ struct print_attribute_debug static void call(std::ostream& out, CategorizedAttr auto const& val) { if constexpr ( - std::disjunction_v< - std::is_pointer, - is_ttp_specialization_of, - is_ttp_specialization_of - > + std::is_pointer_v || + is_ttp_specialization_of_v || + is_ttp_specialization_of_v ) { auto const* ptr = std::to_address(val); if (!ptr) { diff --git a/include/iris/x4/directive/repeat.hpp b/include/iris/x4/directive/repeat.hpp index b260b8819..f03215d6b 100644 --- a/include/iris/x4/directive/repeat.hpp +++ b/include/iris/x4/directive/repeat.hpp @@ -90,10 +90,9 @@ struct repeat_directive : proxy_parser, Subjec using attribute_type = traits::default_container::attribute_type>::type; template - static constexpr bool handles_container = std::disjunction_v< - can_hold::attribute_type, Container>, - can_hold::attribute_type, typename traits::container_value::type> - >; + static constexpr bool handles_container = + can_hold::attribute_type, Container>::value || + can_hold::attribute_type, typename traits::container_value::type>::value; template requires std::is_constructible_v && std::is_constructible_v diff --git a/include/iris/x4/operator/delimited_list.hpp b/include/iris/x4/operator/delimited_list.hpp index b6a06db5b..81967e774 100644 --- a/include/iris/x4/operator/delimited_list.hpp +++ b/include/iris/x4/operator/delimited_list.hpp @@ -28,10 +28,9 @@ struct delimited_list : binary_parser, Left, Right> using attribute_type = traits::default_container::attribute_type>::type; template - static constexpr bool handles_container = std::disjunction_v< - std::bool_constant::template handles_container>, - can_hold::attribute_type, typename traits::container_value::type> - >; + static constexpr bool handles_container = + parser_traits::template handles_container || + can_hold::attribute_type, typename traits::container_value::type>::value; using binary_parser::binary_parser; diff --git a/include/iris/x4/operator/kleene.hpp b/include/iris/x4/operator/kleene.hpp index 5e08087a5..2dbb8a60c 100644 --- a/include/iris/x4/operator/kleene.hpp +++ b/include/iris/x4/operator/kleene.hpp @@ -29,10 +29,9 @@ struct kleene : unary_parser, Subject> using attribute_type = traits::default_container::attribute_type>::type; template - static constexpr bool handles_container = std::disjunction_v< - std::bool_constant::template handles_container>, - can_hold::attribute_type, typename traits::container_value::type> - >; + static constexpr bool handles_container = + parser_traits::template handles_container || + can_hold::attribute_type, typename traits::container_value::type>::value; using unary_parser::unary_parser; diff --git a/include/iris/x4/operator/optional.hpp b/include/iris/x4/operator/optional.hpp index 69ec49d30..ecbf58ad9 100644 --- a/include/iris/x4/operator/optional.hpp +++ b/include/iris/x4/operator/optional.hpp @@ -34,10 +34,9 @@ struct optional : unary_parser, Subject> using attribute_type = build_optional::attribute_type>::type; template - static constexpr bool handles_container = std::disjunction_v< - std::bool_constant::template handles_container>, - can_hold::attribute_type, typename traits::container_value::type> - >; + static constexpr bool handles_container = + parser_traits::template handles_container || + can_hold::attribute_type, typename traits::container_value::type>::value; using unary_parser::unary_parser; diff --git a/include/iris/x4/operator/plus.hpp b/include/iris/x4/operator/plus.hpp index a91b13fa8..f269e4258 100644 --- a/include/iris/x4/operator/plus.hpp +++ b/include/iris/x4/operator/plus.hpp @@ -29,10 +29,9 @@ struct plus : unary_parser, Subject> using attribute_type = traits::default_container::attribute_type>::type; template - static constexpr bool handles_container = std::disjunction_v< - std::bool_constant::template handles_container>, - can_hold::attribute_type, typename traits::container_value::type> - >; + static constexpr bool handles_container = + parser_traits::template handles_container || + can_hold::attribute_type, typename traits::container_value::type>::value; using unary_parser::unary_parser; diff --git a/test/x4/tuple_like.cpp b/test/x4/tuple_like.cpp index 6eeb59215..c11478240 100644 --- a/test/x4/tuple_like.cpp +++ b/test/x4/tuple_like.cpp @@ -44,6 +44,27 @@ struct iris::alloy::adaptor> using getters_list = iris::constant_list{}>; }; +TEST_CASE("tuple-like concepts") +{ + using alloy::tuple; + + STATIC_CHECK(x4::SingleElementTupleLike>); + STATIC_CHECK(x4::SingleElementTupleLike const&>); + STATIC_CHECK(x4::SingleElementTupleLike); + STATIC_CHECK(!x4::SingleElementTupleLike>); + STATIC_CHECK(!x4::SingleElementTupleLike); + + STATIC_CHECK(x4::SingleElementTupleLikeView>); + STATIC_CHECK(!x4::SingleElementTupleLikeView>); + STATIC_CHECK(!x4::SingleElementTupleLikeView>); + STATIC_CHECK(!x4::SingleElementTupleLikeView); + + STATIC_CHECK(x4::SameSizeTupleLike, tuple>); + STATIC_CHECK(!x4::SameSizeTupleLike, tuple>); + STATIC_CHECK(!x4::SameSizeTupleLike>); + STATIC_CHECK(!x4::SameSizeTupleLike, int>); +} + TEST_CASE("unwrap_single_element") { using alloy::tuple; From 0d2c85b6e2dce9c70a8870def0f598d19d8d40e8 Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Sat, 26 Sep 2026 00:57:52 +0900 Subject: [PATCH 6/7] Isolate `is_variant` --- .../x4/core/detail/parse_into_container.hpp | 1 + .../x4/core/traits/attribute_category.hpp | 3 +- include/iris/x4/core/traits/variant_class.hpp | 30 +++++++++++++++++++ .../iris/x4/core/traits/variant_traits.hpp | 11 +------ 4 files changed, 34 insertions(+), 11 deletions(-) create mode 100644 include/iris/x4/core/traits/variant_class.hpp diff --git a/include/iris/x4/core/detail/parse_into_container.hpp b/include/iris/x4/core/detail/parse_into_container.hpp index 1f3ca1798..1d1923b09 100644 --- a/include/iris/x4/core/detail/parse_into_container.hpp +++ b/include/iris/x4/core/detail/parse_into_container.hpp @@ -17,6 +17,7 @@ #include #include +#include #include diff --git a/include/iris/x4/core/traits/attribute_category.hpp b/include/iris/x4/core/traits/attribute_category.hpp index 64f2f46f1..dce2a3796 100644 --- a/include/iris/x4/core/traits/attribute_category.hpp +++ b/include/iris/x4/core/traits/attribute_category.hpp @@ -12,8 +12,9 @@ #include // IWYU pragma: keep +#include #include -#include +#include #include #include diff --git a/include/iris/x4/core/traits/variant_class.hpp b/include/iris/x4/core/traits/variant_class.hpp new file mode 100644 index 000000000..4c1aa0195 --- /dev/null +++ b/include/iris/x4/core/traits/variant_class.hpp @@ -0,0 +1,30 @@ +#ifndef IRIS_ZZ_X4_CORE_TRAITS_VARIANT_CLASS_HPP +#define IRIS_ZZ_X4_CORE_TRAITS_VARIANT_CLASS_HPP + +/*============================================================================= + Copyright (c) 2026 The Iris Project Contributors + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#include // IWYU pragma: keep + +#include + +#include + +namespace iris::x4 { + +template +struct is_variant : std::false_type {}; + +template +inline constexpr bool is_variant_v = is_variant::value; + +template +struct is_variant> : std::true_type {}; + +} // iris::x4 + +#endif diff --git a/include/iris/x4/core/traits/variant_traits.hpp b/include/iris/x4/core/traits/variant_traits.hpp index 94a54f240..2fbb1a425 100644 --- a/include/iris/x4/core/traits/variant_traits.hpp +++ b/include/iris/x4/core/traits/variant_traits.hpp @@ -13,6 +13,7 @@ #include // IWYU pragma: keep #include +#include #include @@ -21,16 +22,6 @@ namespace iris::x4 { -template -struct is_variant : std::false_type {}; - -template -constexpr bool is_variant_v = is_variant::value; - -template -struct is_variant> : std::true_type {}; - - namespace detail { template From 6636c30aa9ff6f19ddb695b851d0c7394ef953d6 Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Sat, 26 Sep 2026 01:15:30 +0900 Subject: [PATCH 7/7] Refactor traits --- include/iris/x4/core/context.hpp | 1 + include/iris/x4/core/detail/parse_into_container.hpp | 1 + include/iris/x4/core/detail/parse_sequence.hpp | 4 ++-- include/iris/x4/core/parser_traits.hpp | 12 ++++++++---- include/iris/x4/core/skip_over.hpp | 4 ++-- include/iris/x4/core/traits/can_hold.hpp | 3 +++ include/iris/x4/core/traits/char_encoding_traits.hpp | 2 ++ include/iris/x4/core/traits/variant_traits.hpp | 2 +- include/iris/x4/directive/no_skip.hpp | 1 + include/iris/x4/directive/repeat.hpp | 4 ++-- include/iris/x4/directive/skip.hpp | 1 + include/iris/x4/directive/with.hpp | 1 + include/iris/x4/numeric/bool.hpp | 1 + include/iris/x4/operator/delimited_list.hpp | 4 +--- include/iris/x4/operator/kleene.hpp | 4 +--- include/iris/x4/operator/optional.hpp | 4 +--- include/iris/x4/operator/plus.hpp | 4 +--- test/x4/char.cpp | 2 -- test/x4/char_class.cpp | 2 -- test/x4/substitution.cpp | 3 +-- 20 files changed, 31 insertions(+), 29 deletions(-) diff --git a/include/iris/x4/core/context.hpp b/include/iris/x4/core/context.hpp index becbe18f1..73debeb42 100644 --- a/include/iris/x4/core/context.hpp +++ b/include/iris/x4/core/context.hpp @@ -12,6 +12,7 @@ #include #include +#include #include diff --git a/include/iris/x4/core/detail/parse_into_container.hpp b/include/iris/x4/core/detail/parse_into_container.hpp index 1d1923b09..d377af12d 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 diff --git a/include/iris/x4/core/detail/parse_sequence.hpp b/include/iris/x4/core/detail/parse_sequence.hpp index 7f7b571ad..0430da00f 100644 --- a/include/iris/x4/core/detail/parse_sequence.hpp +++ b/include/iris/x4/core/detail/parse_sequence.hpp @@ -232,10 +232,10 @@ struct parse_into_container_impl> ) { if constexpr (traits::is_container_v) { - constexpr bool sequence_attribute_can_directly_hold_value_type = can_hold< + constexpr bool sequence_attribute_can_directly_hold_value_type = can_hold_v< typename parser_traits>::attribute_type, typename traits::container_value::type - >::value; + >; if constexpr (sequence_attribute_can_directly_hold_value_type) { return parse_into_container_impl_default>::call(seq, first, last, ctx, attr); diff --git a/include/iris/x4/core/parser_traits.hpp b/include/iris/x4/core/parser_traits.hpp index a7ba115d4..f9c69a09f 100644 --- a/include/iris/x4/core/parser_traits.hpp +++ b/include/iris/x4/core/parser_traits.hpp @@ -47,10 +47,7 @@ struct get_attribute_type template struct get_handles_container { - static constexpr bool value = can_hold< - typename get_attribute_type::type, - Container - >::value; + static constexpr bool value = can_hold_v::type>; }; template @@ -139,6 +136,13 @@ struct parser_traits static constexpr bool need_rcontext = Parser::need_rcontext; }; +// `Parser` writes into `Container`: it fills the container itself, or yields +// one element of it +template +concept WritesIntoContainer = + parser_traits::template handles_container || + can_hold_v::type, typename parser_traits::attribute_type>; + } // iris::x4 #endif diff --git a/include/iris/x4/core/skip_over.hpp b/include/iris/x4/core/skip_over.hpp index 9fa738470..8dbfbc519 100644 --- a/include/iris/x4/core/skip_over.hpp +++ b/include/iris/x4/core/skip_over.hpp @@ -11,14 +11,14 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ==============================================================================*/ -#include +#include // IWYU pragma: keep #include #include #include +#include #include -#include namespace iris::x4 { diff --git a/include/iris/x4/core/traits/can_hold.hpp b/include/iris/x4/core/traits/can_hold.hpp index 1e763a5f9..878764301 100644 --- a/include/iris/x4/core/traits/can_hold.hpp +++ b/include/iris/x4/core/traits/can_hold.hpp @@ -111,6 +111,9 @@ struct can_hold static_assert(X4Attribute); }; +template +inline constexpr bool can_hold_v = can_hold::value; + } // iris::x4 #endif diff --git a/include/iris/x4/core/traits/char_encoding_traits.hpp b/include/iris/x4/core/traits/char_encoding_traits.hpp index 3a787467c..b55c51990 100644 --- a/include/iris/x4/core/traits/char_encoding_traits.hpp +++ b/include/iris/x4/core/traits/char_encoding_traits.hpp @@ -13,6 +13,8 @@ #include +#include + #ifndef IRIS_X4_NO_STANDARD_WIDE # include #endif diff --git a/include/iris/x4/core/traits/variant_traits.hpp b/include/iris/x4/core/traits/variant_traits.hpp index 2fbb1a425..4ad34b338 100644 --- a/include/iris/x4/core/traits/variant_traits.hpp +++ b/include/iris/x4/core/traits/variant_traits.hpp @@ -57,7 +57,7 @@ template struct variant_find_holdable_type_impl { using type = std::conditional_t< - can_hold, T>::value, + can_hold_v, T>, // Given some type `T`, when both `T` and `recursive_wrapper` is seen // during attribute resolution, X4 should ideally materialize the latter diff --git a/include/iris/x4/directive/no_skip.hpp b/include/iris/x4/directive/no_skip.hpp index 373590f55..acf7822f6 100644 --- a/include/iris/x4/directive/no_skip.hpp +++ b/include/iris/x4/directive/no_skip.hpp @@ -14,6 +14,7 @@ #include #include +#include #include #include diff --git a/include/iris/x4/directive/repeat.hpp b/include/iris/x4/directive/repeat.hpp index f03215d6b..c1291b8b4 100644 --- a/include/iris/x4/directive/repeat.hpp +++ b/include/iris/x4/directive/repeat.hpp @@ -91,8 +91,8 @@ struct repeat_directive : proxy_parser, Subjec template static constexpr bool handles_container = - can_hold::attribute_type, Container>::value || - can_hold::attribute_type, typename traits::container_value::type>::value; + can_hold_v::attribute_type> || + can_hold_v::type, typename parser_traits::attribute_type>; template requires std::is_constructible_v && std::is_constructible_v diff --git a/include/iris/x4/directive/skip.hpp b/include/iris/x4/directive/skip.hpp index ee3d9a74f..b5db90b84 100644 --- a/include/iris/x4/directive/skip.hpp +++ b/include/iris/x4/directive/skip.hpp @@ -13,6 +13,7 @@ #include #include +#include #include diff --git a/include/iris/x4/directive/with.hpp b/include/iris/x4/directive/with.hpp index 25e4feadd..648edc161 100644 --- a/include/iris/x4/directive/with.hpp +++ b/include/iris/x4/directive/with.hpp @@ -11,6 +11,7 @@ =============================================================================*/ #include +#include #include #include diff --git a/include/iris/x4/numeric/bool.hpp b/include/iris/x4/numeric/bool.hpp index c27b7a0e8..e1c00524d 100644 --- a/include/iris/x4/numeric/bool.hpp +++ b/include/iris/x4/numeric/bool.hpp @@ -18,6 +18,7 @@ #include #include +#include #include #include diff --git a/include/iris/x4/operator/delimited_list.hpp b/include/iris/x4/operator/delimited_list.hpp index 81967e774..ab091facd 100644 --- a/include/iris/x4/operator/delimited_list.hpp +++ b/include/iris/x4/operator/delimited_list.hpp @@ -28,9 +28,7 @@ struct delimited_list : binary_parser, Left, Right> using attribute_type = traits::default_container::attribute_type>::type; template - static constexpr bool handles_container = - parser_traits::template handles_container || - can_hold::attribute_type, typename traits::container_value::type>::value; + static constexpr bool handles_container = WritesIntoContainer; using binary_parser::binary_parser; diff --git a/include/iris/x4/operator/kleene.hpp b/include/iris/x4/operator/kleene.hpp index 2dbb8a60c..702d0bd87 100644 --- a/include/iris/x4/operator/kleene.hpp +++ b/include/iris/x4/operator/kleene.hpp @@ -29,9 +29,7 @@ struct kleene : unary_parser, Subject> using attribute_type = traits::default_container::attribute_type>::type; template - static constexpr bool handles_container = - parser_traits::template handles_container || - can_hold::attribute_type, typename traits::container_value::type>::value; + static constexpr bool handles_container = WritesIntoContainer; using unary_parser::unary_parser; diff --git a/include/iris/x4/operator/optional.hpp b/include/iris/x4/operator/optional.hpp index ecbf58ad9..6289c382f 100644 --- a/include/iris/x4/operator/optional.hpp +++ b/include/iris/x4/operator/optional.hpp @@ -34,9 +34,7 @@ struct optional : unary_parser, Subject> using attribute_type = build_optional::attribute_type>::type; template - static constexpr bool handles_container = - parser_traits::template handles_container || - can_hold::attribute_type, typename traits::container_value::type>::value; + static constexpr bool handles_container = WritesIntoContainer; using unary_parser::unary_parser; diff --git a/include/iris/x4/operator/plus.hpp b/include/iris/x4/operator/plus.hpp index f269e4258..0bb3a02ae 100644 --- a/include/iris/x4/operator/plus.hpp +++ b/include/iris/x4/operator/plus.hpp @@ -29,9 +29,7 @@ struct plus : unary_parser, Subject> using attribute_type = traits::default_container::attribute_type>::type; template - static constexpr bool handles_container = - parser_traits::template handles_container || - can_hold::attribute_type, typename traits::container_value::type>::value; + static constexpr bool handles_container = WritesIntoContainer; using unary_parser::unary_parser; diff --git a/test/x4/char.cpp b/test/x4/char.cpp index a93606853..7f15d2ed3 100644 --- a/test/x4/char.cpp +++ b/test/x4/char.cpp @@ -45,7 +45,6 @@ TEST_CASE("char") constexpr auto parser = standard::char_('x'); char ch{}; - static_assert(noexcept(parser.parse(first, last, unused, ch))); (void)parser.parse(first, last, unused, ch); // Make sure this is static @@ -57,7 +56,6 @@ TEST_CASE("char") auto const last = sv.end(); constexpr auto parser = unicode::char_(U'x'); char32_t ch{}; - static_assert(noexcept(parser.parse(first, last, unused, ch))); (void)parser.parse(first, last, unused, ch); // Make sure this is static diff --git a/test/x4/char_class.cpp b/test/x4/char_class.cpp index 28127bcc6..32952bcb7 100644 --- a/test/x4/char_class.cpp +++ b/test/x4/char_class.cpp @@ -26,7 +26,6 @@ TEST_CASE("char_class") auto first = sv.begin(); auto const last = sv.end(); char ch{}; - static_assert(noexcept(x4::standard::alnum.parse(first, last, unused, ch))); (void)x4::standard::alnum.parse(first, last, unused, ch); } { @@ -34,7 +33,6 @@ TEST_CASE("char_class") auto first = sv.begin(); auto const last = sv.end(); char32_t ch{}; - static_assert(noexcept(x4::unicode::alnum.parse(first, last, unused, ch))); (void)x4::unicode::alnum.parse(first, last, unused, ch); } diff --git a/test/x4/substitution.cpp b/test/x4/substitution.cpp index d2292cf5e..24a065f6d 100644 --- a/test/x4/substitution.cpp +++ b/test/x4/substitution.cpp @@ -7,8 +7,7 @@ #include -template -inline constexpr bool can_hold_v = x4::can_hold::value; +using x4::can_hold_v; TEST_CASE("can_hold") {