cuda::experimental::stf::sump::sink#

template<class _Reaction>
auto cuda::experimental::stf::sump::sink(
_Reaction &&__reaction,
const ::cuda::std::source_location __loc = ::cuda::std::source_location::current()
) noexcept#

Applies a reaction that is not a sink, or reports a location other than the call site.

A sink is its own carrier: every named policy and every composite accepts the two operators directly, and records the location of the expression that applies it. sink(reaction, loc) is for the rest: a user-defined policy, a raw hook or value (see below), an explicit location, and a carrier that outlives a statement. Two operators apply a sink:

  • policy ->* x evaluates and yields, for when you want the value. An action runs under the policy, so its throws are handled, and its return comes back verbatim, whatever its type: it is never checked and no trait is consulted, even for a registered status type. The right operand must be a callable; a status value goes to the right of <<. ->* never chains.

  • policy << x feeds, possibly several operands: store(&err) << a() << b(). A status value is offered to the policy as with ->*. An action runs under the policy, and what it returns is an error code: a status is fed in turn (a successful one only fixes the chain’s status type), void feeds nothing, and a type without status_traits is a compile error. The expression is a carrier, which records the chain. A sink applied by name is copied into each expression; the library’s stateful sinks keep their state behind a pointer, so the copies share it. A named non-const dynamic is viewed instead of copied, so one model serves every expression that names it; a custom model with mutable state synchronizes its own concurrent use. To keep one chain across statements, name the carrier: auto s = sink(p); s << a; s << b;. C++17 sequences the left side of <<, its handling included, before the right, so each operand runs after the previous one was handled. The carrier records the first status that was forwarded, read with status<S>() (the success value if none was), and forwarded() says whether one was. The first << fixes the chain’s status type, and a later operand of another status type, or status<S>() with another S, is a programming error checked with _CCCL_VERIFY. The carrier converts to nothing implicitly, and a << statement whose status<S>() nobody reads drops a forwarded status: write ->* to keep it.

Both operators bind tighter than & and |, so a composite on their left is parenthesized: (notify & abort) << cudaFree(p). ->* binds tighter than <<, so s << a ->* b parses as s << (a ->* b) and does not compile; write (s << a).status<S>(), or split the statement.

A policy is an object exposing any of two optional capabilities, discovered by compile-time introspection: the exception hook (const std::exception*, source_location, Fn&) whose return value is its answer on the throw path (the callable may be re-invoked by policies like retry; most policies ignore it), and a success hook on_success(...) that observes the result while preserving its type. The named policies include notify, subst, defer, fwd, unwind, retry, as_expected, noop, sump::if_isa, sump::if_monomorphic, sump::if_equal, sump::thrown, sump::returned, when, translate / sump::nest, delay, sump::backoff, remember, circuit_breaker, and sump::always. Guards forward what they do not claim; translators forward with a different exception; delay/backoff/retry re-run; remember serves the last success. Policies compose with & (sequence; the last element answers; non-final answers are discarded) and | (alternation; the left may forward: a guard, translator or predicate on the left forwards by a returned claim, at no cost beyond its own test; any other policy forwards by throwing, at one unwind), and with * (n-fold |).

sink also accepts reactions that are not policies: std::ignore resumes with a default-constructed result; and anything else is taken as a substitution value, exactly as subst(value) (including a user’s nullary nullval-returning ending, which dies silently – pair with notify & to opt the report back in). A substitution passed as an lvalue can serve a reference result, which the policy refers to rather than copies:

int fallback = 42;
int& x = sink(fallback) ->* [] { return returns_a_reference(); }; // x is fallback on a throw

The callable itself must not be noexcept: an exception raised inside one ends the program where it stands, leaving the policy unreachable, so such a pairing is rejected instead of standing there looking like protection. Call such a callable directly.

The location defaults to the call site, for a sink applied directly and for sink(reaction) alike; sink(reaction, loc) reports a different site.

Vocabulary visibility: the named policies live in the non-inline namespace sump. Blessed patterns are a block-scope using namespace cuda::experimental::stf::sump; at the function that configures sinks, or a namespace alias (namespace pol = cuda::experimental::stf::sump;):

using namespace cuda::experimental::stf::sump;
(notify & retry * 3 | subst(-1)) ->* flaky;

namespace pol = cuda::experimental::stf::sump;
pol::subst(0) ->* flaky;

The right operand may also be a status, a value for which status_traits is specialized (cudaError_t, CUresult, … once cuda_safe_call.cuh is included): policy << cudaFree(p). A status is a past result handled on the code channel, and << is the only way to feed one. The carrier then yields it: status<S>() is the first failing status the chain yielded, one an arm forwarded or left unchanged, and forwarded() says whether any arm forwarded. A value an arm substitutes stands in for the status, and unwind turns the status into an exception. Policies that only handle exceptions see the status’s exception, status_traits<S>::to_exception(status, loc). With exceptions disabled, such a policy that may forward (if_isa, a typed store, translate, a user policy without a code-channel hook) reports and aborts on the first failing status, since reaching its hook means throwing; policies with a code-channel hook are unaffected.

Note

When querying noexcept(p ->* f) in a constant expression, write sink(p, loc) ->* f with a location: nvcc’s front-end with a gcc host reports the defaulted source_location::current() argument as potentially throwing, tainting the query (the call itself is noexcept either way).

Parameters:
  • __reaction – [in] The policy (or a reaction normalized into one). A policy is copied or moved in; a raw value or hook passed as an lvalue is referred to.

  • __loc – [in] The location passed to the policy’s hooks.

Returns:

A carrier: it accepts ->* and << like a sink, and a << chain on it records status<S>() and forwarded().