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