examples/fib-memo/ in the source tree. Policy: single_threaded.
This example is about the operator()(args...) overload: it re-runs the callable the moment the argument differs from the stored one, and there is exactly one stored slot.
The program
#include <memoized_invoke.hh>
#include <cstdint>
#include <iostream>
using fedem::utility::memoized_invoke;
namespace
{
std::uint64_t fib_impl( int n )
{
if( n < 2 )
return static_cast< std::uint64_t >( n );
return fib_impl( n - 1 ) + fib_impl( n - 2 );
}
std::uint64_t fib( int n )
{
std::cout << "[compute] fib(" << n << ")\n";
return fib_impl( n );
}
} // namespace
int main( )
{
memoized_invoke fibonacci( &fib, 10 );
std::uint64_t r = 0;
r = fibonacci( ); std::cout << "fib(10) = " << r << '\n'; // computes
r = fibonacci( 10 ); std::cout << "fib(10) = " << r << '\n'; // cache hit
r = fibonacci( 20 ); std::cout << "fib(20) = " << r << '\n'; // cache miss
r = fibonacci( 20 ); std::cout << "fib(20) = " << r << '\n'; // cache hit
r = fibonacci( 10 ); std::cout << "fib(10) = " << r << '\n'; // cache miss
fibonacci.reset( );
r = fibonacci( ); std::cout << "fib(10) = " << r << '\n'; // recompute
return 0;
}
Reading it
Compute into a local, then print. Each line is r = fibonacci(...); std::cout << ... << r; and not std::cout << ... << fibonacci(...);. In a << chain the left operand is sequenced before the right, so the second form would print the label, then run fib (which writes its own [compute] line to the same stream), then the number — interleaving the output. Splitting the call out keeps the transcript clean. (The sibling cparse project's examples hit this exact trap; its smoke tests caught it.)
fibonacci(10) after fibonacci() — the stored argument is 10, the new argument is 10, the tuples compare equal, the state is done → cached, no [compute].
fibonacci(20) — tuples differ. memoized_invoke replaces the stored argument with 20, clears the cache, resets the state, and runs fib(20) → [compute] fib(20).
fibonacci(10) again, after fibonacci(20) — this is the key line. The stored argument is now 20, so 10 is a miss and fib(10) runs a second time. There is one slot, not a 10 → 55, 20 → 6765 table. If you want both cached, keep two memoized_invoke objects.
fibonacci.reset() then fibonacci() — reset() clears the cache and keeps the argument (10), so the nullary operator()() recomputes fib(10).
Trying it
The smoke test pins the transcript:
[compute] fib(10)
fib(10) = 55
fib(10) = 55
[compute] fib(20)
fib(20) = 6765
fib(20) = 6765
[compute] fib(10)
fib(10) = 55
[compute] fib(10)
fib(10) = 55
Four [compute] lines for six calls: first call, argument change to 20, argument change back to 10, and after reset().
See also
- User Guide › The Argument Cache — the equality rule and why there is one slot.
- Reference › memoized_invoke::operator().
- Thread-safe one-time init — the
lock_freepolicy.

