HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Continuation Implementation in C++11

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
implementationcontinuationstackoverflow

Problem

While playing with the concurrency features in C++11 I noticed that there wasn't any support for continuations. I wanted to develop something similar to Tasks in The Parallel Patterns Library (PPL), where a Task can run asynchronously and execute a continuation upon completion using the 'then' member function.

I designed my class around std::future and std::async, which provides the asynchronous processing. The function to run asynchronously accepts any number of parameters and is called using std::async:

template
class FutureWithContinuations final
{
public:
    typedef std::function ContinautionSignature;

    template
    FutureWithContinuations(Function&& futureFunction, ArgTypes&&... args)
        : m_continuations()
        , m_future()
    {
        // Let the concurrency runtime decide whether to run asynchronously or to defer running of the future
        auto future = std::async(std::launch::any, std::forward(futureFunction), std::forward(args)...);
        m_future = future.share();
    }

    template
    void then(const Function& continuationFunction)
    {
        auto continuation = make_unique_continuation(continuationFunction, m_future);
        m_continuations.push_back(std::move(continuation));
    }

    void wait()
    {
        if (!m_future.valid())
            throw std::future_error(std::future_errc::no_state);

        m_future.wait();
        for (const auto& continuation : m_continuations)
        {
            continuation->execute();
        }
        m_continuations.clear();
    }

    T get()
    {
        return m_future.get();
    }

private:
    std::vector> m_continuations;
    std::shared_future m_future;
};


I have two types of continuations, one for when the asynchronous operation produces a result and one for when it doesn't. Both implement an interface that specifies the API for the FutureAndContinuations class to use when dealing with continuations:

```
class IContinuation
{
public:
IContinuation(){

Solution

Not sure what kind of feedback you're looking for here.

What you've implemented is not equivalent to what's usually called .then(). The usual semantics of .then() allow you to do things like

std::future f = std::async([]{ return 1; });
std::future g = f.then([](std::future x){ return x.get() + 1; });
int v = g.get();
assert(v == 1+1);


What you've got, on the other hand, allows you to do something like

std::future f = std::async([]{ return 1; });
f.then([](int x){ printf("%d\n", x); return x + 1; });
int v = f.get();  // prints "1"
assert(v == 1);   // v is not 2


I don't really understand why you'd want the latter semantics. Do you find them useful?

typedef std::function ContinautionSignature;


You misspelled "ContinuationSignature" here; but it doesn't matter, because the type is unused. Remove it.

Your class Continuation is merely a type-erased wrapper around a callable. We have a name for that in C++11 and later: it's called std::function. Consider:

template
std::function make_unique_continuation(
    const ContinuationFunction& continuationFunction,
    const std::shared_future& sharedFuture)
{
    return [=]() mutable { continuationFunction(sharedFuture.get()); };
}


If you do this, about half of your code simply disappears!
(You'll still need to specialize and/or overload the above function a little bit to deal with void, but it looks like you've got the right ideas about that already.)

You may already be aware that in C++14, with proper library support, you can rewrite typename std::enable_if::value>::type as simply std::enable_if_t>.

Code Snippets

std::future<int> f = std::async([]{ return 1; });
std::future<int> g = f.then([](std::future<int> x){ return x.get() + 1; });
int v = g.get();
assert(v == 1+1);
std::future<int> f = std::async([]{ return 1; });
f.then([](int x){ printf("%d\n", x); return x + 1; });
int v = f.get();  // prints "1"
assert(v == 1);   // v is not 2
typedef std::function<void(T)> ContinautionSignature;
template<typename FutureReturnType, typename ContinuationFunction>
std::function<void()> make_unique_continuation(
    const ContinuationFunction& continuationFunction,
    const std::shared_future<FutureReturnType>& sharedFuture)
{
    return [=]() mutable { continuationFunction(sharedFuture.get()); };
}

Context

StackExchange Code Review Q#91269, answer score: 2

Revisions (0)

No revisions yet.