c++c++11stdasyncstd-future

How to create an already-resolved future


I have a function that returns a std::future. I have added a cache to the implementation, and I would like to optionally return a value immediately if it does not need to be recalculated.

How can I create an already-resolved future?

// Class declarations shortened to minimal example to communicate intent
class MyClass {
    Cache m_cache;

    std::future<int> foo(int arg);
}

class Cache {
    std::optional<int> get(int arg);
}

std::future<int> MyClass::foo(int arg) {
    if (auto res = m_cache.get(arg)) {
        // *res is my result
        return std::future(*res); // ????? Doesn't work
    }
    // If the cache misses, we need to calculate it
    // Fire up the ol' thread pool and get to work
    return std::launch(std::launch::async /* ommited for brevity */);
}

I'm targeting C++20.


Solution

  • Only std::async, std::packaged_task and std::promise can create futures with new states.

    std::promise is the easiest way:

    std::future<int> MyClass::foo(int arg) {
        if (auto res = m_cache.get(arg)) {
            // *res is my result
            std::promise<int> p;
            p.set_value(*res);
            return p.get_future();
        }
        // If the cache misses, we need to calculate it
        // Fire up the ol' thread pool and get to work
        return std::launch(std::launch::async /* ommited for brevity */);
    }
    
    // Or with a helper function
    template<typename T>
    std::future<std::decay_t<T>> make_ready_future(T&& value) {
        std::promise<std::decay_t<T>> p;
        p.set_value(std::forward<T>(value));
        return p.get_future();
    }
    
    std::future<int> MyClass::foo(int arg) {
        if (auto res = m_cache.get(arg)) {
            // *res is my result
            return make_ready_future(*res);
        }
        // If the cache misses, we need to calculate it
        // Fire up the ol' thread pool and get to work
        return std::launch(std::launch::async /* ommited for brevity */);
    }