From d1dabe52c5c0ab91df54865c2fcd25dd12e3c21d Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Wed, 25 Feb 2026 17:03:27 +0900 Subject: [PATCH] Add `init_attr` --- include/iris/x4/auxiliary/attr.hpp | 56 +++++++++++++++++++-- include/iris/x4/traits/container_traits.hpp | 8 +++ test/x4/attr.cpp | 23 +++++++++ 3 files changed, 84 insertions(+), 3 deletions(-) diff --git a/include/iris/x4/auxiliary/attr.hpp b/include/iris/x4/auxiliary/attr.hpp index b17e5f31f..0ce1ad43f 100644 --- a/include/iris/x4/auxiliary/attr.hpp +++ b/include/iris/x4/auxiliary/attr.hpp @@ -56,11 +56,11 @@ struct attr_parser : parser> template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool - parse(It&, Se const&, Context const&, Attr& attr) const - noexcept(noexcept(x4::move_to(std::as_const(held_value_), attr))) + parse(It&, Se const&, Context const&, Attr& attr_) const + noexcept(noexcept(x4::move_to(std::as_const(held_value_), attr_))) { // Always copy (need reuse in repetitive invocations) - x4::move_to(std::as_const(held_value_), attr); + x4::move_to(std::as_const(held_value_), attr_); return true; } @@ -68,6 +68,44 @@ struct attr_parser : parser> HeldValueT held_value_; }; +// `init_attr` +template +struct attr_parser : parser> +{ + static_assert(X4Attribute); + static_assert(!X4UnusedAttribute, "attr_parser with `unused_type` is meaningless"); + + using attribute_type = T; + + static constexpr bool handles_container = traits::is_container_v; + + template Se, class Context, X4UnusedAttribute UnusedAttr> + [[nodiscard]] static constexpr bool + parse(It&, Se const&, Context const&, UnusedAttr const&) noexcept + { + return true; + } + + template Se, class Context, X4NonUnusedAttribute ContainerAttr> + requires traits::CategorizedAttr + [[nodiscard]] static constexpr bool + parse(It&, Se const&, Context const&, ContainerAttr& container_attr) noexcept + { + traits::clear(container_attr); + return true; + } + + template Se, class Context, X4NonUnusedAttribute Attr> + requires (!traits::CategorizedAttr) + [[nodiscard]] static constexpr bool + parse(It&, Se const&, Context const&, Attr& attr_) + noexcept(noexcept(attr_ = Attr{})) + { + attr_ = Attr{}; + return true; + } +}; + namespace detail { template @@ -120,11 +158,23 @@ struct attr_gen namespace parsers { +// An always-succeeding parser that has the `attribute_type` equivalent +// to the given parameter. Copies the held instance on each invocation. [[maybe_unused]] inline constexpr detail::attr_gen attr{}; +// A special `attr` parser that resets the variable and always succeeds. +// +// This can be used for constructing `constexpr` instance of a parser +// even when `T` has dynamically allocated storage. +// For example, normal `attr(std::vector{})` cannot be assigned +// to a `constexpr` instance, but `init_attr>` can. +template +[[maybe_unused]] inline constexpr attr_parser init_attr{}; + } // parsers using parsers::attr; +using parsers::init_attr; } // iris::x4 diff --git a/include/iris/x4/traits/container_traits.hpp b/include/iris/x4/traits/container_traits.hpp index c5c9b28fc..f1caa9ff2 100644 --- a/include/iris/x4/traits/container_traits.hpp +++ b/include/iris/x4/traits/container_traits.hpp @@ -133,6 +133,10 @@ namespace detail { struct push_back_fn { + template + static constexpr void + operator()(Container const&, T const&) = delete; + template static constexpr void operator()(Container&, unused_type const&) noexcept { @@ -251,6 +255,10 @@ namespace detail { struct clear_fn { + template + static constexpr void + operator()(Container const&) = delete; + template requires requires(Container& c) { c.clear(); diff --git a/test/x4/attr.cpp b/test/x4/attr.cpp index 01194ff5c..bba786535 100644 --- a/test/x4/attr.cpp +++ b/test/x4/attr.cpp @@ -169,3 +169,26 @@ TEST_CASE("attr") CHECK(ints == std::vector{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2}); } } + +TEST_CASE("init_attr") +{ + using x4::init_attr; + + { + int val = 42; + STATIC_CHECK(std::same_as)>::attribute_type, int>); + REQUIRE(parse("", init_attr, val)); + CHECK(val == 0); + } + { + std::vector val; + val.reserve(100); + val.emplace_back(42); + auto const prev_capacity = val.capacity(); + + STATIC_CHECK(std::same_as>)>::attribute_type, std::vector>); + REQUIRE(parse("", init_attr>, val)); + CHECK(val.empty()); + CHECK(val.capacity() == prev_capacity); // should preserve capacity as per `.clear()` + } +}