17

The code below is based on Herb Sutter's ideas of an implementation of a .then() type continuation.

  template<typename Fut, typename Work>
auto then(Fut f, Work w)->std::future<decltype(w(f.get()))>
  { return std::async([=] { w(f.get()); }); }

This would be used like auto next = then(f, [](int r) { go_and_use(r); }); or similar.

This is a neat idea, but as it stands will not work (futures are move only and not copyable). I do like the idea as it is likely to appear in upcoming versions of c++ as far as I can guess (although as .then() or even await.)

Before making the futures shared or similar I wonder what the stack overflow community would think of this implementation specifically with improvements and suggestions (even shared futures)?

Thanks in advance for any suggestions.

(I am aware this is a fix till a standards based mechanism exists as it will cost a thread (maybe))).

Community
  • 1
  • 1
dirvine
  • 57,399
  • 2
  • 18
  • 19
  • 3
    I'm +1ing because I really appreciated the link to that talk. :-) – Omnifarious Jan 07 '13 at 17:27
  • This is related to a problem I'm trying to solve, which is getting values back from work queues such that work in the original thread is triggered by the availability of a return value. I now suspect I've taken a far too complex approach to the problem. – Omnifarious Jan 08 '13 at 04:08
  • Yes there is a great solution to this as a stopgap till we get async wait/get and I am also searching for that answer. I think this is getting closer but we will see :-) .. – dirvine Jan 08 '13 at 12:44

2 Answers2

9

I find 3 problems with the above implemention:

  • It will only work if you pass std::shared_future as Fut.
  • The continuation might want a chance to handle exceptions.
  • It will not always behave as expected, since if you do not specify std::launch::async it might be deferred, thus the continuation is not invoked as one would expect.

I've tried to address these:

template<typename F, typename W, typename R>
struct helper
{
    F f;
    W w;

    helper(F f, W w)
        : f(std::move(f))
        , w(std::move(w))
    {
    }

    helper(const helper& other)
        : f(other.f)
        , w(other.w)
    {
    }

    helper(helper&& other)
        : f(std::move(other.f))
        , w(std::move(other.w))
    {
    }

    helper& operator=(helper other)
    {
        f = std::move(other.f);
        w = std::move(other.w);
        return *this;
    }

    R operator()()
    {
        f.wait();
        return w(std::move(f)); 
    }
};

}

template<typename F, typename W>
auto then(F f, W w) -> std::future<decltype(w(F))>
{ 
    return std::async(std::launch::async, detail::helper<F, W, decltype(w(f))>(std::move(f), std::move(w))); 
}

Used like this:

std::future<int> f = foo();

auto f2 = then(std::move(f), [](std::future<int> f)
{
    return f.get() * 2; 
});
ronag
  • 49,529
  • 25
  • 126
  • 221
  • Would 'It will not always behave as expected, since if you do not specify std::launch::async it might be deferred, thus the continuation is not invoked as one would expect.' be the case as the .get() would be held in a thread and block will complete (assuming you call a .get() in the final future ? In this case if you had any chaining of the then() all futures would try and get(), hopefully asynchronously unless the implementation was very broken ? – dirvine Jan 07 '13 at 17:48
  • You are correct... assuming you call a .get() in the final future... which you might not necessarily do... and might not expect that you need to. – ronag Jan 07 '13 at 17:50
  • 1
    `const helper&& other` -> `helper&& other`? – GManNickG Jan 07 '13 at 18:27
  • Selecting this as answer as I think it is probably the closest to implementing a continuation type mechanism at this time. I do feel we can possibly get closer but think it's probably good enough to use this answer as a solid approach for now. Thanks very much for the excellent answer particularly with so much detail, very much appreciated. – dirvine Jan 10 '13 at 00:24
  • What compiler did you use? I am using clang (latest SVN version) with libc++ and it won't compile: `No matching function for call to 'then'` – Roman Kutlak Jan 20 '13 at 23:11
  • 1
    This can be fixed in clang by changing the decltypes to std::move the f: `decltype(w(std::move(F))` – Mike Weller Dec 13 '13 at 09:23
-1

Here is the solution, tested with g++ 4.8 and clang++ 3.2:

template<typename F, typename W>
auto then(F&& f, W w) -> std::future<decltype(w(f.get()))>
{
  cout<<"In thread id = "<<std::this_thread::get_id()<<endl;
  return std::async(std::launch::async, w, f.get());
}

void test_then()
{
  std::future<int> result=std::async([]{ return 12;});
  auto f = then(std::move(result), [](int r) {
    cout<<"[after] thread id = "<<std::this_thread::get_id()<<endl;
    cout<<"r = "<<r<<endl;
    return r*r;
  });
  cout<<"Final result f = "<<f.get()<<endl;
}
m1k3l
  • 7
  • 1