15

I've recently discovered, and fallen in love with, the Deferred/Promise pattern used in jQuery. It just encapsulates so many async use cases, including the wonderful chaining, filtering ability, that I can't believe I missed it for so long.

I've just finished refactoring my AS3 code to use the excellent CodeCatalyst/promise-as3 library (https://github.com/CodeCatalyst/promise-as3), and so started thinking about going back to my C++ code and seeing how I could implement the pattern there.

Before I started coding this myself, I checked to see if it had been done before, and discovered the std::future/std::promise (and boost equivalents), but they are very heavy (they seem use real threads etc, and have a heavy template syntax).

So, my question is: Is there are lightweight, pure C++ implementation of the Deferred/Promise pattern, jQuery-style?

refs:

Shmil The Cat
  • 4,548
  • 2
  • 28
  • 37
Shane
  • 3,051
  • 3
  • 40
  • 45
  • 4
    I'm not sure what you mean by "heavy template syntax". Are you asking for a non-templatized version? – Turix Jul 09 '12 at 05:21
  • Have you looked at the header? Check it out, and then try stepping through the debugger to see what I mean. – Shane Jul 09 '12 at 06:55

8 Answers8

15

Sorry to play necromancer, but I too was very interested using A+ style promises in C++ and spent years working out the best way to implement it. I did eventually succeed, and you can see my implementation here.

Usage is pretty straight forward, but does make heavy use of templating and template metaprogramming. Here's an example:

Promise<int> promise;

promise.future().then([](int i){
    std::cout << "i = " << i << std::endl;
    return "foobar";
}).then([](const std::string& str){
    std::cout << "str = " << str << std::endl;
});

promise.resolve(10);

This would print out:

i = 10
str = foobar
Oz.
  • 5,299
  • 2
  • 23
  • 30
  • Could you return a promise from a previous callback in order to chain promise returned by methods within a `then` call? – Loïc Faure-Lacroix Feb 06 '17 at 16:47
  • @LoïcFaure-Lacroix Do you mean something like: Promise promise; promise.future().then([](int i){ std::cout << "i = " << i << std::endl; Promise promStr; someAsyncMethod([promStr]() { promStr.resolve("foobar"); }) return promStr.future(); }).then([](const std::string& str){ std::cout << "str = " << str << std::endl; }); promise.resolve(10); – Oz. Feb 08 '17 at 21:25
  • Comments are not the best for code snippets... but yes. You can return a promise from a then-ed function and the type the promise resolves to is what the next method should expect. – Oz. Feb 08 '17 at 21:28
  • Yes, that's what I meant! That's pretty cool. I haven't had much experience with C++ in years and I can say. Turns out C++ is starting to look like a decent programming language. – Loïc Faure-Lacroix Feb 09 '17 at 06:41
8

I am not sure how lightweight a solution you are after, but std::async simplifies the setting up of future/promise pairs a great deal and allows the caller to decide whether a the work is carried out asynchronously by another thread, or with delayed execution in the same thread. In any case, the caller doesn't have to do any explicit thread management.

juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • 4
    @Shane then you can use `std::async` in delayed execution mode, but this will block your main thread whenever you ask for the result. – juanchopanza Jul 09 '12 at 05:56
  • 1
    I looked up std::async, which looks close. It appears the callback to async is triggered on the returned Future get/wait. This is the wrong way around for what I'm trying to achieve, which is to say, I basically want a bunch of callbacks called when the deferred is set (by me). This is the way jQuery works. Simplistically, it feels like it's effectively a deferred (list head), with list nodes (promises). When the deferred is set (success/fail), the callbacks (proimises) are called. See what I mean? – Shane Jul 09 '12 at 06:16
6

There are a few reasons that I think what you're asking for is almost impossible in C++.

First of all, in order to make use of C++11's new lambda syntax for inline function declaration (the equivalent of which is trivial and very light in JavaScript), you all but have to use templates to consume them.

Secondly, unlike in JavaScript, there's no automatic UI thread for you to park your timer/completion queue on, so you pretty much have to resort to creating a pool of thread(s) that watch for tasks that are ready for the execution of their next step.

What else do you have in mind when you say "pure C++" and "lightweight" (and implicitly threadless)?

sblom
  • 26,911
  • 4
  • 71
  • 95
  • 2
    The idea that a result will be available by some other means in the future, shouldn't depend on a thread. If could be any operation, which may or may not use a thread, such as file I/O or manual poll or whatever. At the end of the day, it's just a linked list of callbacks that get triggered when a Deferred is set. – Shane Jul 09 '12 at 05:52
  • @shane but what would do the work that you are polling for? It sounds like a separate process is acceptable, right? – juanchopanza Jul 09 '12 at 05:59
  • the lambda-link made me kick you in the 10K ;) - congrats – slashmais Jul 09 '12 at 06:22
  • It could/should-be-able-to even be the same process. Consider: You want a function called when Future (trigger) condition has been met. You don't know the result now, so you return a Promise (callback). This promise can be passed around and even used as a factory for another Promise, which all chain together. At some point the condition has been met (via polling, or a timer, or anything at all, in the one and only thread), and so the app sets the future result. This triggers a cascade of promise callbacks. Hopefully that makes a bit more sense. – Shane Jul 09 '12 at 06:25
1

You can use a cooperative multitasker. I'm using one in my application. The only trouble I am having is with captured variables getting nuked if I store a lambda inside a list<> and call it later. I have still not figured out a solution, but I'm quite certain that it should be possible to do this.

Martin
  • 3,509
  • 3
  • 26
  • 31
1

I know this question is old, however I suppose there is worth to mention also rili, which probably implements exactly what you want.

There are some examples and documentation.

The same like lifewanted(liblw) it is based heavily on templates. One of major features of this library is A+ Promises implementation - including Then(single and two parameter), Catch and Finally.

Links:

  • Is it still exist? Homepage gives me 404 – odiszapc Jan 25 '19 at 06:18
  • yep. It was moved to: https://gitlab.com/rilis/rili/promise - last year I realized that set of my libraries is constantly growing, so independent parts were moved to separate repositories. – Tomasz Frydrych Jan 29 '19 at 17:18
1

Here is another C++ promise library that mimics JavaScript promises. It is designed to be lightweight so it can be used e.g. very I/O intensive async software, and at the same time have as many of the goodies that JS promises have. It has been used for years in a very popular project, so it's battle-proven: https://github.com/alxvasilev/cpp-promise

Alexander Vassilev
  • 1,399
  • 13
  • 23
0

Just use Facebook's Folly library: https://github.com/facebook/folly/blob/master/folly/docs/Futures.md

Boost.Thread and C++17 (does not even support combinators yet) do not offer as much functionality as Folly. In Folly you can even receive the successful results instead of Try like shown in the answer of user Oz. You can limit the number of threads by using a thread pool executor (this was previously part of Wangle but is now part of Folly) or even an inline executor. Without templates you won't get far in C++ and they ensure type safety which is a good thing.

Bear in mind that you have to use move semantics for futures but you can use a folly::SharedPromise to create multiple futures from one promise.

You can also look at this list: https://en.wikipedia.org/wiki/Futures_and_promises#List_of_implementations ("Non-standard library based implementations of futures:").

Baradé
  • 1,290
  • 1
  • 15
  • 35
0

Use JavaScript-like Promises for C++20. It relies on C++20 coroutines, supports ES6 await/async semantics, and very importantly, it supports 'move' so you can write wrappers for frameworks like asio (e.g. because asio::ip::tcp::socket cannot be copied).

Link: https://github.com/virgil382/JSLikePromise