Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 53 additions & 3 deletions include/iris/x4/auxiliary/attr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,56 @@ struct attr_parser : parser<attr_parser<T, HeldValueT>>

template<std::forward_iterator It, std::sentinel_for<It> 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;
}

private:
HeldValueT held_value_;
};

// `init_attr<T>`
template<class T>
struct attr_parser<T, void> : parser<attr_parser<T, void>>
{
static_assert(X4Attribute<T>);
static_assert(!X4UnusedAttribute<T>, "attr_parser with `unused_type` is meaningless");

using attribute_type = T;

static constexpr bool handles_container = traits::is_container_v<T>;

template<std::forward_iterator It, std::sentinel_for<It> Se, class Context, X4UnusedAttribute UnusedAttr>
[[nodiscard]] static constexpr bool
parse(It&, Se const&, Context const&, UnusedAttr const&) noexcept
{
return true;
}

template<std::forward_iterator It, std::sentinel_for<It> Se, class Context, X4NonUnusedAttribute ContainerAttr>
requires traits::CategorizedAttr<ContainerAttr, traits::container_attr>
[[nodiscard]] static constexpr bool
parse(It&, Se const&, Context const&, ContainerAttr& container_attr) noexcept
{
traits::clear(container_attr);
return true;
}

template<std::forward_iterator It, std::sentinel_for<It> Se, class Context, X4NonUnusedAttribute Attr>
requires (!traits::CategorizedAttr<Attr, traits::container_attr>)
[[nodiscard]] static constexpr bool
parse(It&, Se const&, Context const&, Attr& attr_)
noexcept(noexcept(attr_ = Attr{}))
{
attr_ = Attr{};
return true;
}
};

namespace detail {

template<traits::CharArray R>
Expand Down Expand Up @@ -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<int>{})` cannot be assigned
// to a `constexpr` instance, but `init_attr<std::vector<int>>` can.
template<class T>
[[maybe_unused]] inline constexpr attr_parser<T, void> init_attr{};

} // parsers

using parsers::attr;
using parsers::init_attr;

} // iris::x4

Expand Down
8 changes: 8 additions & 0 deletions include/iris/x4/traits/container_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ namespace detail {

struct push_back_fn
{
template<class Container, class T>
static constexpr void
operator()(Container const&, T const&) = delete;

template<class Container>
static constexpr void operator()(Container&, unused_type const&) noexcept
{
Expand Down Expand Up @@ -251,6 +255,10 @@ namespace detail {

struct clear_fn
{
template<class Container>
static constexpr void
operator()(Container const&) = delete;

template<class Container>
requires requires(Container& c) {
c.clear();
Expand Down
23 changes: 23 additions & 0 deletions test/x4/attr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,26 @@ TEST_CASE("attr")
CHECK(ints == std::vector<int>{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<x4::parser_traits<decltype(init_attr<int>)>::attribute_type, int>);
REQUIRE(parse("", init_attr<int>, val));
CHECK(val == 0);
}
{
std::vector<int> val;
val.reserve(100);
val.emplace_back(42);
auto const prev_capacity = val.capacity();

STATIC_CHECK(std::same_as<x4::parser_traits<decltype(init_attr<std::vector<int>>)>::attribute_type, std::vector<int>>);
REQUIRE(parse("", init_attr<std::vector<int>>, val));
CHECK(val.empty());
CHECK(val.capacity() == prev_capacity); // should preserve capacity as per `.clear()`
}
}