C++ promise/A+ library in Javascript style.

Overview

C++ promise/A+ library in Javascript style.

What is promise-cpp ?

Promise-cpp is header only library that implements promise/A+ standard.

Promise-cpp is easy to use, just #include "promise.hpp" is enough. With promise-cpp, you can resolve or reject any type of data without writing complex template code.

Promise-cpp is designed to built by c++11 compilers and almost no dependencies. Although some of the examples are linked against boost library, promise-cpp itself is absolutely workable without boost library and can be used together with other asynchronized libraries.

Promise-cpp can be the base component in event-looped asychronized programming, which is NOT std::promise.

Examples

Examples list

Please check folder "build" to get the codelite/msvc projects for the test code above, or use cmake to build from CMakeLists.txt.

Compiler required

The library has passed test on these compilers --

  • gcc 5

  • Visual studio 2015 sp3

  • clang 3.4.2

Build tips

Some of the examples use boost::asio as io service, and use boost::beast as http service. You need to install boost_1_66 or higher to build the examples.

For examples, on windows, you can build boost library in these steps --

> cd *boost_source_folder*
> bootstrap.bat
> b2.exe stage variant=release runtime-link=static threading=multi
> b2.exe stage variant=debug runtime-link=static threading=multi

After have boost installed, modify path of boost library in the example's project file according to the real path.

Sample code 1

This sample code shows converting a timer callback to promise object.

#include <stdio.h>
#include <boost/asio.hpp>
#include "asio/timer.hpp"

using namespace promise;
using namespace boost::asio;

/* Convert callback to a promise (Defer) */
Defer myDelay(boost::asio::io_service &io, uint64_t time_ms) {
    return newPromise([&io, time_ms](Defer &d) {
        setTimeout(io, [d](bool cancelled) {
            if (cancelled)
                d.reject();
            else
                d.resolve();
        }, time_ms);
    });
}


Defer testTimer(io_service &io) {

    return myDelay(io, 3000).then([&] {
        printf("timer after 3000 ms!\n");
        return myDelay(io, 1000);
    }).then([&] {
        printf("timer after 1000 ms!\n");
        return myDelay(io, 2000);
    }).then([] {
        printf("timer after 2000 ms!\n");
    }).fail([] {
        printf("timer cancelled!\n");
    });
}

int main() {
    io_service io;

    Defer timer = testTimer(io);

    delay(io, 4500).then([=] {
        printf("clearTimeout\n");
        clearTimeout(timer);
    });

    io.run();
    return 0;
}

Sample code 2

This sample code shows promise resolve/reject flows.

#include <stdio.h>
#include <string>
#include "promise.hpp"

using namespace promise;

#define output_func_name() do{ printf("in function %s, line %d\n", __func__, __LINE__); } while(0)

void test1() {
    output_func_name();
}

int test2() {
    output_func_name();
    return 5;
}

void test3(int n) {
    output_func_name();
    printf("n = %d\n", n);
}

Defer run(Defer &next){

    return newPromise([](Defer d){
        output_func_name();
        d.resolve(3, 5, 6);
    }).then([](const int &a, int b, int c) {
        printf("%d %d %d\n", a, b, c);
        output_func_name();
    }).then([](){
        output_func_name();
    }).then([&next](){
        output_func_name();
        next = newPromise([](Defer d) {
            output_func_name();
        });
        //Will call next.resole() or next.reject() later
        return next;
    }).then([](int n, char c) {
        output_func_name();
        printf("n = %d, c = %c\n", (int)n, c);
    }).fail([](char n){
        output_func_name();
        printf("n = %d\n", (int)n);
    }).fail([](short n) {
        output_func_name();
        printf("n = %d\n", (int)n);
    }).fail([](int &n) {
        output_func_name();
        printf("n = %d\n", (int)n);
    }).fail([](const std::string &str) {
        output_func_name();
        printf("str = %s\n", str.c_str());
    }).fail([](uint64_t n) {
        output_func_name();
        printf("n = %d\n", (int)n);
    }).then(test1)
    .then(test2)
    .then(test3)
    .always([]() {
        output_func_name();
    });
}

int main(int argc, char **argv) {
    Defer next;

    run(next);
    printf("======  after call run ======\n");

    next.resolve(123, 'a');
    //next.reject('c');
    //next.reject(std::string("xhchen"));
    //next.reject(45);

    return 0;
}

Global functions

Defer newPromise(FUNC func);

Creates a new Defer object with a user-defined function. The user-defined functions, used as parameters by newPromise, must have a parameter Defer d. for example --

return newPromise([](Defer d){
})

Defer resolve(const RET_ARG... &ret_arg);

Returns a promise that is resolved with the given value. for example --

return resolve(3, '2');

Defer reject(const RET_ARG... &ret_arg);

Returns a promise that is rejected with the given arguments. for example --

return reject("some_error");

Defer all(const PROMISE_LIST &promise_list);

Wait until all promise objects in "promise_list" are resolved or one of which is rejected. The "promise_list" can be any container that has Defer as element type.

for (Defer &defer : promise_list) { ... }

for example --

Defer d0 = newPromise([](Defer d){ /* ... */ });
Defer d1 = newPromise([](Defer d){ /* ... */ });
std::vector<Defer> promise_list = { d0, d1 };

all(promise_list).then([](){
    /* code here for all promise objects are resolved */
}).fail([](){
    /* code here for one of the promise objects is rejected */
});

Defer race(const PROMISE_LIST &promise_list);

Rturns a promise that resolves or rejects as soon as one of the promises in the iterable resolves or rejects, with the value or reason from that promise. The "promise_list" can be any container that has Defer as element type.

for (Defer &defer : promise_list) { ... }

for example --

Defer d0 = newPromise([](Defer d){ /* ... */ });
Defer d1 = newPromise([](Defer d){ /* ... */ });
std::vector<Defer> promise_list = { d0, d1 };

race(promise_list).then([](){
    /* code here for one of the promise objects is resolved */
}).fail([](){
    /* code here for one of the promise objects is rejected */
});

Defer doWhile(FUNC func);

"While loop" for promisied task. A promise(Defer) object will passed as parameter when call func, which can be resolved to continue with the "while loop", or be rejected to break from the "while loop".

for example --

doWhile([](Defer d){
    // Add code here for your task in "while loop"
    
    // Call "d.resolve();" to continue with the "while loop",
    
    // or call "d.reject();" to break from the "while loop", in this case,
    // the returned promise object will be in rejected status.
});

Class Defer - type of promise object.

class Defer is the type of promise object.

Defer::resolve(const RET_ARG... &ret_arg);

Resolve the promise object with arguments, where you can put any number of ret_arg with any type. (Please be noted that it is a method of Defer object, which is different from the global resolve function.) for example --

return newPromise([](Defer d){
    //d.resolve();
    //d.resolve(3, '2', std::string("abcd"));
    d.resolve(9567);
})

Defer::reject(const RET_ARG... &ret_arg);

Reject the promise object with arguments, where you can put any number of ret_arg with any type. (Please be noted that it is a method of Defer object, which is different from the global reject function.) for example --

return newPromise([](Defer d){
    //d.reject();
    //d.reject(std::string("oh, no!"));
    d.reject(-1, std::string("oh, no!"))
})

Defer::then(FUNC_ON_RESOLVED on_resolved, FUNC_ON_REJECTED on_rejected)

Return the chaining promise object, where on_resolved is the function to be called when previous promise object calls function resolve, on_rejected is the function to be called when previous promise object calls function reject. for example --

return newPromise([](Defer d){
    d.resolve(9567, 'A');
}).then(

    /* function on_resolved */ [](int n, char ch){
        printf("%d %c\n", n, ch);   //will print 9567 here
    },

    /* function on_rejected */ [](){
        printf("promise rejected\n"); //will not run to here in this code 
    }
);

Defer::then(FUNC_ON_RESOLVED on_resolved)

Return the chaining promise object, where on_resolved is the function to be called when previous promise object calls function resolve. for example --

return newPromise([](Defer d){
    d.resolve(9567);
}).then([](int n){
    printf("%d\n", n);  b //will print 9567 here
});

Defer::fail(FUNC_ON_REJECTED on_rejected)

Return the chaining promise object, where on_rejected is the function to be called when previous promise object calls function reject.

This function is usually named "catch" in most implements of Promise library. https://www.promisejs.org/api/

In promise_cpp, function name "fail" is used instead of "catch", since "catch" is a keyword of c++.

for example --

return newPromise([](Defer d){
    d.reject(-1, std::string("oh, no!"));
}).fail([](int err, string &str){
    printf("%d, %s\n", err, str.c_str());   //will print "-1, oh, no!" here
});

Defer::finally(FUNC_ON_FINALLY on_finally)

Return the chaining promise object, where on_finally is the function to be called whenever the previous promise object is be resolved or rejected.

The returned promise object will keeps the resolved/rejected state of current promise object.

for example --

return newPromise([](Defer d){
    d.reject(std::string("oh, no!"));
}).finally([](){
    printf("in finally\n");   //will print "in finally" here
});

Defer::always(FUNC_ON_ALWAYS on_always)

Return the chaining promise object, where on_always is the function to be called whenever the previous promise object is be resolved or rejected.

The returned promise object will be in resolved state whenever current promise object is resolved or rejected.

for example --

return newPromise([](Defer d){
    d.reject(std::string("oh, no!"));
}).always([](){
    printf("in always\n");   //will print "in always" here
});

And more ...

about exceptions

To throw any object in the callback functions above, including on_resolved, on_rejected, on_always, will same as d.reject(the_throwed_object) and returns immediately. for example --

return newPromise([](Defer d){
    throw std::string("oh, no!");
}).fail([](string &str){
    printf("%s\n", str.c_str());   //will print "oh, no!" here
});

For the better performance, we suggest to use function reject instead of throw.

about the chaining parameter

Any type of parameter can be used when call resolve, reject or throw, except that the plain string or array. To use plain string or array as chaining parameters, we may wrap it into an object.

newPromise([](Defer d){
    // d.resolve("ok"); may cause a compiling error, use the following code instead.
    d.resolve(std::string("ok"));
})

Match rule for chaining parameters

"then" and "fail" function can accept multiple promise parameters and they follows the below rule --

Resolved parameters

Resolved parameters must match the next "then" function, otherwise it will throw an exception and can be caught by the following "fail" function.

Rejected parameters

First let's take a look at the rule of c++ try/catch, in which the thrown value will be caught in the block where value type is matched. If type in the catch block can not be matched, it will run into the default block catch(...) { }.

try{
    throw (short)1;
}catch(int a){
    // will not go to here
}catch(short b){
    // (short)1 will be caught here
}catch(...){
    // will not go to here
}

"Promise-cpp" implement "fail" chain as the match style of try/catch.

newPromise([](Defer d){
    d.reject(3, 5, 6);
}).fail([](std::string str){
    // will not go to here since parameter types are not match
}).fail([](const int &a, int b, int c) {
    // d.reject(3, 5, 6) will be caught here
}).fail([](){
    // Will not go to here sinace previous rejected promise was caught.
});

Omit parameters

The number of parameters in "then" or "fail" chain can be lesser than that's in resolve function.

newPromise([](Defer d){
    d.resolve(3, 5, 6);
}).then([](int a, int b) {
    // d.resolve(3, 5, 6) will be caught here since a, b matched with the resolved parameters and ignore the 3rd parameter.
});

A function in "then" chain without any parameters can be used as default promise caught function.

newPromise([](Defer d){
    d.resolve(3, 5, 6);
}).then([]() {
    // Function without parameters will be matched with any resolved values,
    // so d.resolve(3, 5, 6) will be caught here.
});

The reject parameters follows the the same omit rule as resolved parameters.

copy the promise object

To copy the promise object is allowed and effective, please do that when you need.

Defer d = newPromise([](Defer d){});
Defer d1 = d;  //It's safe and effective

handle uncaught exceptional or rejected parameters

The uncaught exceptional or rejected parameters are ignored by default. We can specify a handler function to do with these parameters --

handleUncaughtException([](Defer &d) {
    d.fail([](int n, int m) {
        //go here if the uncaught parameters match types "int n, int m".
    }).fail([](char c) {
        //go here if the uncaught parameters match type "char c".
    }).fail([]() {
        //go here for all other uncaught parameters.
    });
});

about multithread

The "Defer" object is not thread safe by default for better performance.

To make it workable with multithread, define PM_MULTITHREAD before include "promise.hpp"

#define PM_MULTITHREAD
#include "promise.hpp"

working in embedded chips ?

Yes, it works! Please use promise_embed, which is the special version optimized for embedded chips, such as Cortex M0/M3(STM32, etc...).

Promise_embed provides multitask function in single thread even through there's no operation system.

Comments
  • Move header files into a subfolder

    Move header files into a subfolder

    I'd like to include this library in the vcpkg package catalog (https://github.com/microsoft/vcpkg/pull/20340) but ran into an issue that would be nice to get addressed here in the library instead of having to be patched on the vcpkg side.

    Currently, the library puts all its header files directly under include. This could be problematic because there may be clashes with other libraries that use the same names for their include files, especially since some of the headers use generic names like add_ons.hpp.

    I suggest to put all headers into a subfolder promise-cpp or similar, and refer to them via #include "promise-cpp/...hpp" in all files.

    opened by chausner 3
  • Possible memory leaks

    Possible memory leaks

    First of all, thank you for the perfect lib! Promise implementation is good and clean. Looking into the lib deeply.

    After running this simple line code using Catch framework with valgrind --leak-check=full --show-leak-kinds=all :

    TEST_CASE("Test") {
        promise::Defer promise = promise::newPromise();
    }
    

    two "still reachable" warnings was thrown:

    =3030== 24 bytes in 1 blocks are still reachable in loss record 1 of 2
    ==3030==    at 0x4C2F1CA: operator new(unsigned long) (vg_replace_malloc.c:334)
    ==3030==    by 0x7A3C56: promise::pm_memory_pool* promise::pm_stack_new<promise::pm_memory_pool, unsigned long>(unsigned long&&) (stack.hpp:122)
    ==3030==    by 0x7A390A: promise::pm_size_allocator<56ul>::get_memory_pool() (allocator.hpp:94)
    ==3030==    by 0x7A347A: void* promise::pm_allocator::obtain<promise::Promise>() (allocator.hpp:195)
    ==3030==    by 0x7A2DBD: promise::Promise* promise::pm_new<promise::Promise>() (allocator.hpp:216)
    ==3030==    by 0x7A2913: promise::newPromise() (promise.hpp:1221)
    ==3030==    by 0x933DA9: ____C_A_T_C_H____T_E_S_T____8() (promise_test.cpp:126)
    ==3030==    by 0x8E0089: Catch::FreeFunctionTestCase::invoke() const (catch_test_case_registry_impl.hpp:150)
    ==3030==    by 0x8BAF1C: Catch::TestCase::invoke() const (catch_test_case_info.hpp:176)
    ==3030==    by 0x8DEE48: Catch::RunContext::invokeActiveTestCase() (catch_run_context.hpp:367)
    ==3030==    by 0x8DEBEB: Catch::RunContext::runCurrentTest(std::string&, std::string&) (catch_run_context.hpp:340)
    ==3030==    by 0x8DD934: Catch::RunContext::runTest(Catch::TestCase const&) (catch_run_context.hpp:131)
    ==3030==
    ==3030== 88 bytes in 1 blocks are still reachable in loss record 2 of 2
    ==3030==    at 0x4C2F8B7: operator new[](unsigned long) (vg_replace_malloc.c:423)
    ==3030==    by 0x7A1A48: promise::pm_allocator::obtain_impl(promise::pm_memory_pool*, unsigned long) (allocator.hpp:118)
    ==3030==    by 0x7A348F: void* promise::pm_allocator::obtain<promise::Promise>() (allocator.hpp:196)
    ==3030==    by 0x7A2DBD: promise::Promise* promise::pm_new<promise::Promise>() (allocator.hpp:216)
    ==3030==    by 0x7A2913: promise::newPromise() (promise.hpp:1221)
    ==3030==    by 0x933DA9: ____C_A_T_C_H____T_E_S_T____8() (promise_test.cpp:126)
    ==3030==    by 0x8E0089: Catch::FreeFunctionTestCase::invoke() const (catch_test_case_registry_impl.hpp:150)
    ==3030==    by 0x8BAF1C: Catch::TestCase::invoke() const (catch_test_case_info.hpp:176)
    ==3030==    by 0x8DEE48: Catch::RunContext::invokeActiveTestCase() (catch_run_context.hpp:367)
    ==3030==    by 0x8DEBEB: Catch::RunContext::runCurrentTest(std::string&, std::string&) (catch_run_context.hpp:340)
    ==3030==    by 0x8DD934: Catch::RunContext::runTest(Catch::TestCase const&) (catch_run_context.hpp:131)
    ==3030==    by 0x8B8321: Catch::runTests(Catch::Ptr<Catch::Config> const&) (catch_session.hpp:82)
    

    I guess both point to custom memory allocator:

    The first one:

    promise::pm_memory_pool* promise::pm_stack_new<promise::pm_memory_pool, unsigned long>(unsigned long&&) (stack.hpp:122)
    

    The second:

    promise::pm_allocator::obtain_impl(promise::pm_memory_pool*, unsigned long) (allocator.hpp:118)
    

    In both cases I see memory allocation which are once allocated (fix me if I'm wrong) will never be freed.

    I'm not sure this is 100% memory leak, but in more complex production examples instead of "still reachable" I see "possibly lost" or even "definitely lost" warnings pointing to the same lines of code.

    Could you please clarify this issue, I saw static thread_local memory pooling logic inside, so maybe we could implement some function whose responsibility would be to clear all the pools manually to make valgrind happy? Like this:

    TEST_CASE("Test") {
        promise::Defer promise = promise::newPromise();
        promise::pm_allocator::free_pools();
    }
    

    Thank you.

    opened by odiszapc 2
  • Add overload to use Defer::then(Defer) with rvalues.

    Add overload to use Defer::then(Defer) with rvalues.

    Hello, thank you for sharing your code.

    I'd like to call then(f()) with a function that returns a promise. My suggestion is to add an overload for Defer&&, but anything that makes the included test pass is fine with me.

    I'd also suggest to provide an "install" target.

    Happy new year, Paul

    opened by pck 2
  • Project structure rearranged.

    Project structure rearranged.

    Sorry, I think I took out too much last PR with the PROMISE_BUILD_EXAMPLES option. I did go a bit further and rearranged the structure slightly. Now I can do the following

    include(FetchContent)
    FetchContent_Declare(
        promise
        GIT_REPOSITORY https://github.com/xhawk18/promise-cpp.git
        GIT_TAG        e41e25c9b078141ff2e499cd6a2678386b67311e
    )
    FetchContent_MakeAvailable(promise)
    
    add_executable(myProgram ...)
    target_link_library(myProgram PRIVATE promise)
    
    #include <promise-cpp/promise.hpp>
    // Notice that these are now directly available, and can be used when boost is found:
    #include <promise-cpp/add_ons/asio/timer.hpp>
    
    int main()
    {
      setTimeout(...)
    }
    
    opened by 5cript 1
  • Include dir is now public and example builds optional.

    Include dir is now public and example builds optional.

    target_link_library(mything PRIVATE promise)

    is not sufficient, because promise does not provide its include directory to the using target. I changed that.

    I also made the example builds optional via an option, so I can omit them in my project. Please crosscheck that I didnt opt-out too much!

    opened by 5cript 1
  • fix: warning: declaration of 'lock' shadows a previous local [-Wshadow]

    fix: warning: declaration of 'lock' shadows a previous local [-Wshadow]

    I swore I submitted this as a PR, but for the life of me I cannot find it. This removes a warning for a lock that is shadowed. In theory I shouldn't be an issue, but I noticed a few errors in helgrind warning about mutex locks forming out of order. 99.9% sure its boost asio, but this caught my eye.

    opened by geiseri 0
  • promise::all(...) cannot resolve to native vector

    promise::all(...) cannot resolve to native vector

    Greetings, I have the following code:

    
    std::vector<Promise> promise_list = { 
      promise::resolve<float>(10),
      promise::resolve<float>(11),
    };
    
    all(promise_list).then([](const std::vector<float> &vals){
        /* code here for all promise objects are resolved */
    }).fail([](){
        /* code here for one of the promise objects is rejected */
    });
    

    This compiles but bails with a bad any_cast execption. The following though works:

    std::vector<Promise> promise_list = { 
    promise::resolve<float>(10),
    promise::resolve<float>(11),
    };
    
    all(promise_list).then([](const std::vector<any> &vals){
       std::vector<float> res{};
       std::transform(vals.begin(), vals.end(), std::back_inserter(res),
                               [](const promise::any &r) { return r.cast<float>(); });
      return res;
    }).then([](const std::vector<float> &vals){
        /* code here for all promise objects are resolved as floats */
    }).fail([](){
        /* code here for one of the promise objects is rejected */
    });
    

    I am wondering if you can use the container std::vector<T>::value_type for the cast from the promise into the function. I am not sure if this would break other stuff though since it would need to happen after resolution. This would also break instances where you have multiple types returned by the promises. Thoughts?

    opened by geiseri 2
  • Cannot Use Mutable Lambda

    Cannot Use Mutable Lambda

    The following does not work using clang 14

    auto promise = newPromise([](Defer d){ /*...*/ });
    promise.then([bla]() mutable {
    });
    

    with "call_traits.hpp:135:20: error: call to pointer to member function of type 'void ()' drops 'const' qualifier"

    opened by 5cript 0
  • Assertion error in multi-threaded programs

    Assertion error in multi-threaded programs

    Hi, I'm trying to use promise-cpp in multi-threaded programs, but sometimes the following assertion error occurs in promise-cpp.

    promise_inl.hpp(161)

                std::list<std::shared_ptr<Task>> &pendingTasks = promiseHolder->pendingTasks_;
                //promiseHolder->dump();
                assert(pendingTasks.front() == task); // Assertion failed: (pendingTasks.front() == task), function call, file promise_inl.hpp, line 161.
                pendingTasks.pop_front();
                //promiseHolder->dump();
    

    The README document says promise-cpp is thread safe but It seems not thread safe in some situation.

    Is this a bug? Or should I fix how I use promise-cpp? Thanks.

    Step to reproduce:

    Build and run this code snippet.

    #include <iostream>
    #include <memory>
    #include <vector>
    #include <thread>
    #include <functional>
    #include <mutex>
    #include <atomic>
    #include <deque>
    #include <stdexcept>
    
    #define PROMISE_HEADONLY
    #include <promise-cpp/promise.hpp>
    
    struct ThreadPool
    {
        ThreadPool() {}
        ~ThreadPool() {}
    
        void startThreads(int num) {
            stopThreads();
            _shouldStop = false;
            for(int i = 0; i < num; ++i) {
                auto th = std::thread([this] { threadProcess(); });
                _threads.push_back(std::move(th));
            }
        }
    
        void stopThreads() {
            _shouldStop = true;
            std::for_each(_threads.begin(), _threads.end(),
                          [](auto &th) { th.join(); });
        }
    
        void addTask(std::function<void()> f) {
            auto lock = std::unique_lock<std::mutex>(_mtx);
            _tasks.push_back(f);
            _cvEmpty.notify_all();
        }
    
        void threadProcess()
        {
            for( ; ; ) {
                if(_shouldStop) { break; }
    
                std::function<void()> f;
                {
                    auto lock = std::unique_lock<std::mutex>(_mtx);
    
                    auto result = _cvEmpty.wait_for(lock, std::chrono::milliseconds(100), [&] {
                        return _tasks.size() > 0;
                    });
    
                    if(result == false) { continue; }
    
                    f = _tasks.front();
                    _tasks.pop_front();
                }
    
                f();
            }
        }
    
    private:
        std::mutex _mtx;
        std::deque<std::function<void()>> _tasks;
        std::vector<std::thread> _threads;
        std::atomic<bool> _shouldStop = { false };
        std::condition_variable _cvEmpty;
    
    public:
        static ThreadPool * getInstance() { return _instance.load(); }
        static ThreadPool * setInstance(ThreadPool *inst) { return _instance.exchange(inst); }
    
    private:
        static inline std::atomic<ThreadPool *> _instance;
    };
    
    promise::Promise yield_async()
    {
        return promise::newPromise([](promise::Defer defer) {
            ThreadPool::getInstance()->addTask([defer] {
                defer.resolve();
            });
        });
    }
    
    int main()
    {
        std::cout << "Start." << std::endl;
        ThreadPool pool;
        ThreadPool::setInstance(&pool);
    
        pool.startThreads(10);
    
        auto finished = std::make_shared<bool>(false);
    
        static auto func = [=] {
            return yield_async().then([] {
                return true;
            });
        };
    
        {
            auto count = std::make_shared<std::atomic<int>>(0);
    
            promise::doWhile([=](promise::DeferLoop loop) {
                if(*count >= 300) {
                    loop.doBreak(count->load());
                    return;
                }
    
                promise::newPromise([=](promise::Defer defer) {
                    func().then([=](bool b) {
                        bool x = b;
                    }).then([=] {
                        return yield_async();
                    }).then([=] {
                        defer.resolve();
                    });
    
                }).then([=] {
                    *count += 1;
                    std::cout << "count: " << *count << std::endl;
                    yield_async().then([=] {
                        loop.doContinue();
                    });
                });
            }).then([=](int n) {
                std::cout << "hello : " << n << std::endl;
                *finished = true;
            });
        };
    
        pool.stopThreads();
        ThreadPool::setInstance(nullptr);
    
        std::cout << "Finished." << std::endl;
        return 0;
    }
    
    bug 
    opened by hotwatermorning 3
  • promise::any貌似不支持右值构造

    promise::any貌似不支持右值构造

    struct Test {
        Test() {
            printf("constructor\n");
        };
    
        Test(const Test &) {
            printf("copy constructor\n");
        }
    
        Test(Test && m) {
            printf("move constructor\n");
        }
    
        ~Test() {
            printf("destruct \n");
        }
    
    };
    int main() {
        promise::any a(Test{});
    //    输出
    //    constructor
    //    copy constructor
    //    destruct
    //    destruct
    }
    

    C++17 的std::any是正常的,输出

    constructor
    move constructor
    destruct 
    destruct 
    

    看起来是holder类没有对右值构造进行重载。

    template<typename ValueType>
        class holder : public placeholder {
        public: // structors
            holder(const ValueType & value)
                : held(value) {
            }
    
        public: // queries
            virtual type_index type() const {
                return type_id<ValueType>();
            }
    
            virtual placeholder * clone() const {
                return new holder(held);
            }
    
            virtual any call(const any &arg) const {
                return any_call(held, arg);
            }
        public: // representation
            ValueType held;
        private: // intentionally left unimplemented
            holder & operator=(const holder &);
        };
    
    opened by Cirnoo 1
  • 嵌套promise在析构时候会抛异常

    嵌套promise在析构时候会抛异常

    test0.cpp中有如下示例

    .then([&next](){
            output_func_name();
            next= newPromise([](Defer d) {
                output_func_name();
                //尝试调用d.resolve(1, 'c');
            });
            //尝试调用 next.resolve();,或next.resolve(1, 'c');
            //Will call next.resole() or next.reject() later
            //throw 33;
            //next.reject(55, 66);
            return next;
        })
    

    如果增加next.resolve();或者d.resolve(); 会产生bad_any_cast异常,导致promiseHolder->state_ = TaskState::kRejected,在析构时会走到全局异常handler中。

    我想通过创建一个新的newPromise的方式,内部切线程异步处理完成后再回调到下个then,这样看起来会有问题。

    opened by Cirnoo 3
Releases(2.1.5)
Owner
菩提本無樹,明鏡亦非臺
null
APSI is a C++ library for Asymmetric (unlabeled or labeled) Private Set Intersection.

Private Set Intersection (PSI) refers to a functionality where two parties, each holding a private set of items, can check which items they have in common without revealing anything else to each other. Upper bounds on the sizes of the sets are assumed to be public information and are not protected.

Microsoft 112 Jan 6, 2023
A toolchain designed to build a DRM-free version of Rifts: Promise of Power for the Nokia N-Gage.

Rifts: Promise of Power A toolchain designed to build a DRM-free version of Rifts: Promise of Power for the Nokia N-Gage. How-to First clone the repos

Michael Fitzmayer 4 Mar 27, 2022
🎵 Music notation engraving library for MEI with MusicXML and Humdrum support and various toolkits (JavaScript, Python)

Verovio is a fast, portable and lightweight library for engraving Music Encoding Initiative (MEI) digital scores into SVG images. Verovio also contain

RISM Switzerland 519 Jan 1, 2023
Cross-platform C++ library providing a simple API to read and write INI-style configuration files

simpleini A cross-platform library that provides a simple API to read and write INI-style configuration files. It supports data files in ASCII, MBCS a

Brodie Thiesfield 797 Dec 28, 2022
Cross-platform, graphics API agnostic, "Bring Your Own Engine/Framework" style rendering library.

bgfx - Cross-platform rendering library GitHub Discussions Discord Chat What is it? Cross-platform, graphics API agnostic, "Bring Your Own Engine/Fram

Бранимир Караџић 12.6k Jan 8, 2023
:sparkles: Magical headers that make your C++ library accessible from JavaScript :rocket:

Quick start | Requirements | Features | User guide | Contributing | License nbind is a set of headers that make your C++11 library accessible from Jav

charto 1.9k Dec 26, 2022
Functional programming style pattern-matching library for C++

Mach7: Pattern Matching for C++ by Yuriy Solodkyy, Gabriel Dos Reis, Bjarne Stroustrup Abstract Pattern matching is an abstraction mechanism that can

Yuriy Solodkyy 1.2k Dec 26, 2022
The libxo library allows an application to generate text, XML, JSON, and HTML output using a common set of function calls. The application decides at run time which output style should be produced.

libxo libxo - A Library for Generating Text, XML, JSON, and HTML Output The libxo library allows an application to generate text, XML, JSON, and HTML

Juniper Networks 253 Dec 10, 2022
Structy is an irresponsibly dumb and simple struct serialization/deserialization library for C, Python, and vanilla JavaScript.

Structy Structy is an irresponsibly dumb and simple struct serialization/deserialization library for C, Python, and vanilla JavaScript. You can think

Stargirl Flowers 53 Dec 29, 2022
A golang-style C++ coroutine library and more.

CO is an elegant and efficient C++ base library that supports Linux, Windows and Mac platforms. It pursues minimalism and efficiency, and does not rely on third-party library such as boost.

Alvin 3.1k Jan 5, 2023
fmtlog is a performant fmtlib-style logging library with latency in nanoseconds.

fmtlog fmtlog is a performant asynchronous logging library using fmt library format. Features Faster - lower runtime latency than NanoLog and higher t

Meng Rao 443 Jan 6, 2023
A simple library that helps Android developers to execute JavaScript code from Android native side easily without using Webview.

AndroidJSModule A simple library that helps Android developers to execute JavaScript code from Android native side easily without using Webview. Insta

Hung Nguyen 5 May 24, 2022
A library in Javascript to create graphs in the browser similar to Unreal Blueprints.

A graph node engine and editor written in Javascript similar to PD or UDK Blueprints, comes with its own editor in HTML5 Canvas2D. The engine can run client side or server side using Node. It allows to export graphs as JSONs to be included in applications independently.

Javi Agenjo 3.3k Jan 3, 2023
Vizzu is a free, open-source Javascript/C++ library for animated data visualizations and data stories.

Vizzu is a free, open-source Javascript/C++ library utilizing a generic dataviz engine that generates many types of charts and seamlessly animates between them

Vizzu 1.6k Jan 3, 2023
Google's common Java, C++ and JavaScript library for parsing, formatting, and validating international phone numbers.

What is it? Google's common Java, C++ and JavaScript library for parsing, formatting, and validating international phone numbers. The Java version is

Google 14.5k Dec 30, 2022
A go-style coroutine library in C++11 and more.

cocoyaxi English | 简体中文 A go-style coroutine library in C++11 and more. 0. Introduction cocoyaxi (co for short), is an elegant and efficient cross-pla

Alvin 3.1k Dec 27, 2022
High level HTTP Request Library that gives a javascript fetch like API.

Fetch for Arduino fetch is a high level HTTP Request Library that gives you a javascript fetch like API. ResponseOptions options; options.method = "PO

ma.name 67 Dec 28, 2022
Library with useful output stream tools like: color and style manipulators, progress bars and terminal graphics.

Library with useful output stream tools like: color and style manipulators, progress bars and terminal graphics Table of contents Introduction Documen

Gianluca Bianco 168 Dec 20, 2022
Lightweight, Portable, Flexible Distributed/Mobile Deep Learning with Dynamic, Mutation-aware Dataflow Dep Scheduler; for Python, R, Julia, Scala, Go, Javascript and more

Apache MXNet (incubating) for Deep Learning Apache MXNet is a deep learning framework designed for both efficiency and flexibility. It allows you to m

The Apache Software Foundation 20.2k Dec 31, 2022
Go-style concurrency in C

LIBMILL Libmill is a library that introduces Go-style concurrency to C. Documentation For the documentation check the project website: http://libmill.

Martin Sustrik 2.6k Dec 31, 2022