diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 3c2728b..3716d01 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -17,7 +17,7 @@ jobs: run: | mkdir build cd build - g++ -std=c++20 -Wall -Wextra -Wconversion -Werror -I../include ../tests/memo_cache.cpp -o memo_cache + g++ -std=c++23 -Wall -Wextra -Wconversion -Werror -I../include ../tests/memo_cache.cpp -o memo_cache - name: Test run: | diff --git a/include/memo_cache.hpp b/include/memo_cache.hpp index a1b1f50..7b0ee27 100644 --- a/include/memo_cache.hpp +++ b/include/memo_cache.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -146,6 +147,43 @@ class memo_cache { return replace_and_shift(key, f(key)); } + /// Get a value, or, if it does not exist in the cache, insert it using the value computed by f. + /// Returns a result with a reference to the found, or newly inserted value associated with the given key. + /// If f fails, the error is returned. + /// If a value is inserted, the key is cloned. + /// + /// # Examples + /// + /// ``` + /// #include + /// #include + /// + /// mc::memo_cache c; + /// + /// assert(!c.contains(42)); + /// + /// auto v1 = c.find_or_try_insert_with(42, + /// [] -> std::expected { return "The Answer"; }); + /// + /// assert(v1 == "The Answer"); + /// assert(c.find(42).has_value()); + /// assert(c.find(42).value() == "The Answer"); + /// + /// auto v2 = c.find_or_try_insert_with(42, + /// [] -> std::expected { return std::unexpected{"Dunno"}; }); + /// + /// assert(!v2.has_value()); + /// assert(v2.error() == "Dunno"); + /// ``` + template + [[nodiscard]] std::expected, Error> find_or_try_insert_with(const Key& key, std::function(const Key&)> &&f) { + if (auto slot = find(key); slot) { + return *slot; + } + + return f(key).transform([&](auto v) { return replace_and_shift(key, std::move(v)); }); + } + /// Returns `true` if the cache contains a value for the specified key. /// /// # Examples