Defined in header <memoized_invoke.hh>

class single_threaded;

single_threaded is the default ExecutionPolicy for memoized_invoke — the one CTAD selects. It tracks the run state in a plain, non-atomic enum member (not_started / running / done). Every operation is a single load or store; there is no overhead over a hand-written bool ran_ flag.

It is not thread-safe. Two threads calling operator() on the same memoized_invoke can both observe not_started and both run the callable. Use it when the object is confined to one thread, when an external lock already serialises access, or when you only want the return-value cache and reset() and the one-time guarantee comes from elsewhere (a function-local static, say). For a first-run race across threads, use lock_free.

Member functions

FunctionEffect
bool try_enter()If the state is done, returns false. Otherwise sets it to running and returns true.
void mark_done() noexceptSets the state to done.
void mark_free() noexceptSets the state to not_started (used by reset() and by exception rollback).
bool is_done() const noexcepttrue when the state is done.
void wait() const noexceptNo-op — a single-threaded caller is never actually blocked.

Special members

Default-, copy- and move-constructible and copy- and move-assignable, all implicitly defined. Copy and move copy the state like any enum member — so a copy of a memoized_invoke<single_threaded, …> that is done is itself done and serves the cached value without re-running. (This is the opposite of lock_free, whose copy/move reset the state.)

<a id="the-executionpolicy-interface"></a>The ExecutionPolicy interface

memoized_invoke uses its policy only through these five members. Any type that provides them — with these semantics — can be the policy:

ExpressionReturnsSemantics
p.try_enter()boolAtomically move not-startedrunning. true iff this call won the race.
p.mark_done()voidMove to done. Wake any waiters.
p.mark_free()voidMove to not-started (reset / rollback). Wake any waiters.
p.is_done()booltrue while in the done state.
p.wait()voidBlock until the state is no longer running, then return (the caller re-checks).

The type must be default-, copy- and move-constructible and copy- and move-assignable. Copy and move may re-initialise the state. lock_free is the reference implementation of a synchronising policy; a std::mutex + std::condition_variable version is a short exercise (see User Guide › Execution Policies).

Example

#include <memoized_invoke.hh>
#include <cassert>

using fedem::utility::memoized_invoke;
using fedem::utility::single_threaded;

int main()
{
    memoized_invoke co( []{ return 7; } );   // single_threaded, deduced by CTAD
    assert( co() == 7 );
    assert( co.is_done() );

    auto copy = co;              // state copied — copy is already done
    assert( copy.is_done() );
    assert( copy() == 7 );       // no re-run
}

See also