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
Sto 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
sto 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
nanosenderviananosender_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-voidtypenanosender_traits<std::remove_cvref_t<S>>::connect(std::move(s), std::move(recv))– Must create ananooperationby connecting the nanosendersto the nanoreceiverrecv
-
S &&s#
-
template<typename S, typename T>
concept nanosender_of# Matches a
nanosenderSwhose 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
nanosenderand associatednanoreceiver.
Functions#
-
template<nanosender S, nanoreceiver_for<S> R>
nanooperation auto connect(
)# Connects a nanosender
sto 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 invocablehandlermust return a newnanosenderwhen invoked with the value sent bys.The overload
[[2]]oflet()that accepts only ahandlerreturns 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_targument and must return ananosenderobject.
- Returns:
A new nanosender \(S_{\tt ret}\), which sends
sends_t<invoke_result_t<H, sends_t<S>>>
When
scompletes, thehandlerwill be invoked with the result fromsto 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 fromhandlerwill be the new value that is sent by \(S_{\tt ret}\).The overload
[[2]]ofthen()that accepts only ahandlerreturns 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
nanosenderto 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
Predicatetype is a predicate that determines when to accept a value from the input senders. Afirst_completedsender is equivalent to afirst_wherethat 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_completedsupports 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
justwill 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, thenjustwill hold a copy of that value.If
justis copy-connected, then the heldTwill be copied into the operation state as aT. (Copy-connecting ajustrequires thatTbe copy-constructible.)If
justis move-connected, then the heldTwill be moved into the operation state as aT.
Hint
Beware that passing an lvalue via CTAD to
justwill cause thejustto 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::moveto 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
justan 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
recvcannot be inlined within a box.recv – A nanoreceiver. Must be a receiver for either an
emitter_resultor aresult<unique_box>.
- Returns:
A new
unique_handler\(H\)
When the handler \(H\) is
completed, the status and value are bound in either anemitter_resultor 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
sndcannot be inlined within a box.snd – A
nanosender. Must send anemitter_result.
- Returns:
A new
unique_emitter\(E\).
When the sender
sndcompletes with anemitter_result\(R\), thestatusandvaluefrom \(R\) will be passed toamongoc_handler_complete().