Header lexy/callback/container.hpp
Callbacks and sinks for constructing containers.
Callback and sink lexy::as_list and lexy::as_collection
lexy/callback/container.hppnamespace lexy
{
template <typename Container>
struct as-container
{
//=== callback ===//
using return_type = Container;
constexpr Container operator()(lexy::nullopt) const;
constexpr Container operator()(Container&& container) const;
template <typename ... Args>
constexpr Container operator()(Args&&... args) const;
template <typename ... Args>
constexpr Container operator()(const Container::allocator_type& allocator,
Args&&... args) const;
//=== sink ===//
struct sink-callback
{
using return_type = Container;
template <typename T>
constexpr void operator()(T&& obj);
template <typename ... Args>
constexpr void operator()(Args&&... args);
constexpr Container finish() &&;
};
constexpr sink-callback sink() const;
constexpr sink-callback sink(const Container::allocator_type& allocator) const;
//=== allocator ===//
template <typename AllocatorFn>
constexpr auto allocator(AllocatorFn allocator_fn) const;
constexpr auto allocator() const
{
return allocator(identity-fn);
}
};
template <typename Container>
constexpr as-container<Container> as_list;
template <typename Collection>
constexpr as-container<Container> as_collection;
}Callbacks and sink to construct the given Container.
as_list is used for positional containers like std::vector.
It calls .push_back() and .emplace_back().
as_collection is used for non-positional containers like std::set.
It calls .insert() and .emplace().
As a callback, they have the following overloads:
(lexy::nullopt)Returns an empty container by default constructing it.
(Container&& container)Forwards an existing container unchanged.
(Args&&… args)Repeatedly calls
.push_back()/.insert()on an empty container; if called withNarguments, the resulting container will containNitems. If.reserve(sizeof…(args))is well-formed, calls that first. The final container is returned.(const typename Container::allocator_type& allocator, Args&&… args)Same as above, but constructs the empty container using the given
allocator.
As a sink, .sink() can be called with zero arguments or with one argument of type Container::allocator_type.
In the first case, it default constructs an empty container.
In the second case, it constructs it using the allocator.
The resulting sink callback has the following overloads and returns the finished container:
(T&& object)Calls
.push_back()/.insert()on the container.(Args&&… args)Calls
.emplace_back()/.emplace()on the container.
The .allocator() function takes a function that obtains the allocator from the parse state.
If the function is not provided, it uses the parse state itself as the allocator.
It returns a new callback and sink that accepts the parse state.
It then has the same overloads and behaviors, except that it will always use the allocator obtained via the allocator_fn from the parse state.
As a callback, this behavior is similar to lexy::bind where the allocator is bound via lexy::parse_state.
As a sink, this behavior is similar to lexy::bind_sink where the allocator is bound via lexy::parse_state.
struct state
{
std::allocator<int> allocator; // The allocator that should be used.
// Potentially other members here.
};
struct production
{
static constexpr auto whitespace = dsl::ascii::space;
static constexpr auto rule = [] {
auto integer = dsl::integer<int>;
return dsl::list(integer, dsl::sep(dsl::comma));
}();
static constexpr auto value
// Pass the allocator to the sink.
= lexy::as_list<std::vector<int>>.allocator(&state::allocator);
};Callback and sink lexy::concat
lexy/callback/container.hppnamespace lexy
{
template <typename Container>
struct concat-impl
{
using return_type = Container;
constexpr Container operator()() const;
constexpr Container operator()(lexy::nullopt) const;
constexpr Container operator()(Container&& head, Container&&... tail) const;
constexpr sink auto sink() const;
};
template <typename Container>
constexpr concat-impl concat;
}Callback and sink that concatenates multiple containers.
As a callback, it accepts zero or more existing containers, and will concatenate them together.
This is done by repeatedly calling .append() or .push_back() on the first container.
As a sink, it creates a default constructed container object as the current result.
It can then be invoked with a single container object.
If the current result is still empty, the new container is move assigned into it.
Otherwise, the new container is appended by calling .append() or .push_back().
struct list
{
static constexpr auto rule = [] {
auto integer = dsl::integer<int>;
return dsl::list(integer, dsl::sep(dsl::comma));
}();
static constexpr auto value = lexy::as_list<std::vector<int>>;
};
struct production
{
static constexpr auto whitespace = dsl::ascii::space;
static constexpr auto rule = dsl::list(dsl::p<list>, dsl::sep(dsl::newline));
static constexpr auto value = lexy::concat<std::vector<int>>;
};Sink lexy::collect
lexy/callback/container.hppnamespace lexy
{
constexpr sink<> auto collect(callback auto&& callback)
requires std::is_void_v<callback-return-type>;
template <typename Container>
constexpr sink<> auto collect(callback auto&& callback);
requires !std::is_void_v<callback-return-type>;
}Turns a callback into a sink by collecting all results.
The first overload requires that the callback returns void.
It returns a sink that repeatedly invokes callback and produces the number of invocations as a std::size_t.
The second overload requires that the callback does not return void.
.sink() can be called with zero arguments or with one argument of type Container::allocator_type.
In the first case, it default constructs an empty container; in the second case, it constructs it using the allocator.
The sink callback just forwards to callback and adds the result to the container by calling .push_back().
The final container is returned.
Note | See lexy::callback for the inverse operation that turns a sink into a callback. |
Tip | Use collect for the error callback. It will collect all errors into a single container. |