Nanosenders#
Warning
This page is for amongoc
developers and the documented components and
behavior are not part of any public API guarantees.
amongoc
contains a modified subset of the P2300 Senders+Receivers library
known as nanosenders. This name reflects the reduced/simplified feature set
and API surface.
Traits & Types#
-
template<typename S>
struct nanosender_traits# Allows customization of a type
S
to behave as ananosender
-
template<nanoreceiver_of<sends_type> R>
nanooperation auto connect(
) [[1]]# -
template<nanoreceiver_of<sends_type> R>
nanooperation connect(
) [[2]]# Connects a nanosender
s
to a nanoreceiverr
.Overload #1 calls std::move(s).connect(std::forward<R>(r)) (Move-connects)
Overload #2 calls s.connect(std::forward<R>(r)) (Copy-connects)
Both overloads are constrained on whether their associated expression is valid. This call will return a
nanooperation
.
-
template<nanoreceiver_of<sends_type> R>
-
template<typename S>
using sends_t# Get the type that will be sent by a
nanosender
viananosender_traits
. Yields the typenanosender_traits<std::remove_cvref_t<S>>::sends_type
.
-
template<typename T>
class archetype_nanoreceiver# A type that implements
nanoreceiver_of
Concepts#
-
template<typename S>
concept nanosender# -
S &&s#
-
archetype_nanoreceiver<sends_t<S>> recv#
Requires:
typename sends_t<S>
must name a non-void
typenanosender_traits<std::remove_cvref_t<S>>::connect(std::move(s), std::move(recv))
– Must create ananooperation
by connecting the nanosenders
to the nanoreceiverrecv
-
S &&s#
-
template<typename S, typename T>
concept nanosender_of# Matches a
nanosender
S
whose sends_t<S> is convertible toT
.
-
template<typename R, typename T>
concept nanoreceiver_of = std::invocable<R, T># The object must be invocable with the given value as its sole argument. The receiver and value will be perfect-forwarded for the invocation.
A nanoreceiver can safely assume that it will only be invoked once.
-
template<typename R, typename S>
concept nanoreceiver_for = nanosender<S> and nanoreceiver_of<sends_t<S>>#
-
template<typename O>
concept nanooperation# A type that holds the operation state of a connected
nanosender
and associatednanoreceiver
.
Functions#
-
template<nanosender S, nanoreceiver_for<S> R>
nanooperation auto connect(
)# Connects a nanosender
s
to a nanoreceiverr
. Perfect-forwards each argument. Returns a new operation state.Note
This is an invocable object, not a function template
-
template<nanosender S, std::invocable<sends_t<S>> H>
requires nanosender<std::invoke_result_t<H, sends_t<S>>>
nanosender auto let(
) [[1]]# -
auto let(auto &&handler) [[2]]#
Create a continuation sender \(S_{\tt ret}\) for the nanosender
s
. The invocablehandler
must return a newnanosender
when invoked with the value sent bys
.The overload
[[2]]
oflet()
that accepts only ahandler
returns a closure object that can be used as the right-hand size of anoperator|
. The expressions | let(h)
is equivalent tolet(s, h)
.- Parameters:
s – A nanosender to be continued.
handler – A handler function that must accept a
sends_t
argument and must return ananosender
object.
- Returns:
A new nanosender \(S_{\tt ret}\), which sends
sends_t<invoke_result_t<H, sends_t<S>>>
When
s
completes, thehandler
will be invoked with the result froms
to obtain a newnanosender
\(S'\).\(S'\) will be immediately
connect()
ed to another receiver to form a newnanooperation
\(O'\), which will be started immediately to continue the composed operation. The result value sent by \(S'\) will be re-sent via \(S_{\tt ret}\).This is the C++ equivalent of
amongoc_let()
(andamongoc_let()
is implemented in terms oflet()
).
-
template<nanosender S, std::invocable<sends_t<S>> H>
nanosender auto then(
) [[1]]# -
auto then(auto &&handler) [[2]]#
Create a continuation sender \(S_{\tt ret}\) for the nanosender
s
. The return value fromhandler
will be the new value that is sent by \(S_{\tt ret}\).The overload
[[2]]
ofthen()
that accepts only ahandler
returns a closure object that can be used as the right-hand size of anoperator|
. The expressions | then(h)
is equivalent tothen(s, h)
.- Parameters:
s – A
nanosender
to be composed.handler – A handler function that must be invocable with
sends_t<S>
, which returns a \(T\).
- Returns:
A new
nanosender
\(S_{\tt ret}\) that sends a \(T\).
Classes#
-
template<typename Predicate, nanosender... S>
class first_where# -
template<nanosender... S>
class first_completed# Provides a
nanosender
\(S\) that completes with a std::variant<sends_t<S...>> \(V\), where the active alternative in \(V\) corresponds to the nanosender \(S\) which first completed.The
Predicate
type is a predicate that determines when to accept a value from the input senders. Afirst_completed
sender is equivalent to afirst_where
that accepts every value value it sees.When the first value is accepted, all other pending nanosenders will be cancelled immediately. \(S\) will only resolve once all input senders resolve, so it is essential that the input senders respect cancellation otherwise the operation for \(S\) will stall waiting for the senders to complete normally.
CTAD
first_completed
supports CTAD, and is recommended for most cases.
-
template<typename T>
class just# Provides a
nanosender
\(S\) that immediately completes with aT
. The connected receiver will be invoked within thestart()
call on the resulting operation.Note
The stored value will be perfect-forwarded and supports reference types for
T
:If given an lvalue \(x\), then
just
will store an lvalue reference to \(x\). When it completes, the receiver will be passed an lvalue reference to that \(x\).If given an r-value of type
T
, thenjust
will hold a copy of that value.If
just
is copy-connected, then the heldT
will be copied into the operation state as aT
. (Copy-connecting ajust
requires thatT
be copy-constructible.)If
just
is move-connected, then the heldT
will be moved into the operation state as aT
.
Hint
Beware that passing an lvalue via CTAD to
just
will cause thejust
to hold a reference to that lvalue:auto foo() { std::string h = "Hello!"; return just(h); // UB!! The returned just() holds a reference to `h`! }
If you have an lvalue that you want to give ownership to a
just
, usestd::move
to give the object to thejust
:std::string some_string = xyz(); auto J = just(std::move(some_string)); // J now owns the `some_string`
If you want to give
just
an independent copy without moving-from the object, useauto()
to force a copy:std::string some_string = xyz(); auto J = just(auto(some_string)); // J owns a copy of `some_string`
C API Compatibilty#
The nanosender
APIs are not part of the public API, but are used to implement
it.
unique_emitter
is a nanosender
#
The unique_emitter
type acts as a nanosender
which sends an
emitter_result
value.
When a nanoreceiver_of<emitter_result> is connected a unique_emitter
,
the C++ receiver type will be converted to a unique_handler
using
as_handler()
.
unique_handler
is a nanoreceiver_of<emitter_result>#
A unique_handler
object can be used as a receiver of emitter_result
via
its unique_handler::operator()()
.
Adaptors#
-
unique_handler as_handler(mlib::allocator<> a, auto &&recv)#
Creates a
unique_handler
\(H\) from a C++ nanoreceiver.- Parameters:
a – An allocator for the handler’s state. Only used if
recv
cannot be inlined within a box.recv – A nanoreceiver. Must be a receiver for either an
emitter_result
or aresult<unique_box>
.
- Returns:
A new
unique_handler
\(H\)
When the handler \(H\) is
completed
, the status and value are bound in either anemitter_result
or aresult<unique_box>
(whichever is expected byrecv
) and then passed torecv
.
-
unique_emitter as_emitter(mlib::allocator<> a, nanosender auto &&snd)#
Create a
unique_emitter
\(E\) from a C++ nanosender.- Parameters:
a – An allocator for the emitter’s state. Only used if
snd
cannot be inlined within a box.snd – A
nanosender
. Must send anemitter_result
.
- Returns:
A new
unique_emitter
\(E\).
When the sender
snd
completes with anemitter_result
\(R\), thestatus
andvalue
from \(R\) will be passed toamongoc_handler_complete()
.