aboutsummaryrefslogblamecommitdiff
path: root/promise.hpp
blob: 397ef70de9cf0e1a41c3e8d7acda375be0f4f36f (plain) (tree)





















































                                                                                                             



                                                          







                          

                                                                                     
















                                                    








                                                    



















































                                                                       
              

                                           
              











































                                                                                       
                                                                                  













                                                                               
                                                                                   













                                                                           

                                                                   











                                            

                                                                  







































































































                                                                                       
#ifndef _CPPROMISE_HPP
#define _CPPROMISE_HPP

#include <vector>
#include <memory>
#include <any>
#include <functional>

/** Implement type-safe Promise primitives similar to the ones specified by
 * Javascript A+. */
namespace promise {
    using std::function;
    using pm_any_t = std::any;
    using None = std::nullptr_t;
    using values_t = std::vector<pm_any_t>;
    const auto none = nullptr;
    const auto do_nothing = [](){};

    class Promise;
    class promise_t {
        std::shared_ptr<Promise> ptr;
        public:
        promise_t(function<void(promise_t)> callback) {
            ptr = std::make_shared<Promise>();
            callback(*this);
        }

        template<typename T> inline void resolve(T result) const;
        template<typename T> inline void reject(T reason) const;

        template<typename F_, typename F = pm_any_t, typename R_, typename R = pm_any_t>
        inline promise_t then(function<F_(pm_any_t)> fulfilled_callback,
                        function<R_(pm_any_t)> rejected_callback) const;

        template<typename F_, typename F = pm_any_t>
        inline promise_t then(function<F_(pm_any_t)> fulfilled_callback) const;

        template<typename R_, typename R = pm_any_t>
        inline promise_t fail(function<R_(pm_any_t)> rejected_callback) const;

        template<typename F_, typename F>
        inline promise_t then(function<F_(F)> on_fulfilled) const;

        template<typename F_, typename F, typename R_, typename R>
        inline promise_t then(function<F_(F)> on_fulfilled, function<R_(R)> on_rejected) const;

        template<typename F_, typename F>
        inline promise_t fail(function<F_(F)> on_fulfilled) const;
    };

#define PROMISE_ERR_INVALID_STATE do {throw std::runtime_error("invalid promise state");} while (0)
#define PROMISE_ERR_MISMATCH_TYPE do {throw std::runtime_error("mismatching promise value types");} while (0)
    
    class Promise {
        //function<void()> fulfilled_callback;
        //function<void()> rejected_callback;
        std::vector<function<void()>> fulfilled_callbacks;
        std::vector<function<void()>> rejected_callbacks;
        enum class State {
            Pending,
            Fulfilled,
            Rejected,
        } state;
        pm_any_t result;
        pm_any_t reason;

        /* this implementation causes stack overflow because of the nested lambdas */
        /*
        void add_on_fulfilled(function<void()> cb) {
            auto old_cbs = fulfilled_callback;
            fulfilled_callback = function<void()>(
                [cb, old_cbs]() {
                    old_cbs();
                    cb();
                });
        }

        void add_on_rejected(function<void()> cb) {
            auto old_cbs = rejected_callback;
            rejected_callback = function<void()>(
                [cb, old_cbs]() {
                    old_cbs();
                    cb();
                });
        }
        */

        void add_on_fulfilled(function<void()> cb) {
            fulfilled_callbacks.push_back(cb);
        }

        void add_on_rejected(function<void()> cb) {
            rejected_callbacks.push_back(cb);
        }

        function<void()> gen_on_fulfilled(
                function<promise_t(pm_any_t)> on_fulfilled,
                const promise_t &npm) {
            return [this, on_fulfilled, npm]() {
                on_fulfilled(result).then(
                    function<None(pm_any_t)>([npm] (pm_any_t result_) {
                        npm.resolve(result_);
                        return none;
                    }),
                    function<None(pm_any_t)>([npm] (pm_any_t reason_) {
                        npm.reject(reason_);
                        return none;
                    }));
            };
        }

        function<void()> gen_on_fulfilled(
                function<pm_any_t(pm_any_t)> on_fulfilled,
                const promise_t &npm) {
            return [this, on_fulfilled, npm]() {
                npm.resolve(on_fulfilled(result));
            };
        }

        function<void()> gen_on_rejected(
                function<promise_t(pm_any_t)> on_rejected,
                const promise_t &npm) {
            return [this, on_rejected, npm]() {
                on_rejected(reason).then(
                    function<None(pm_any_t)>([npm] (pm_any_t result_) {
                        npm.resolve(result_);
                        return none;
                    }),
                    function<None(pm_any_t)>([npm] (pm_any_t reason_) {
                        npm.reject(reason_);
                        return none;
                    }));
            };
        }

        function<void()> gen_on_rejected(
                function<pm_any_t(pm_any_t)> on_rejected,
                const promise_t &npm) {
            return [this, on_rejected, npm]() {
                npm.reject(on_rejected(reason));
            };
        }

        public:

        Promise():
            /*
            fulfilled_callback(do_nothing),
            rejected_callback(do_nothing),
            */
            state(State::Pending) {
            //printf("%lx constructed\n", (uintptr_t)this);
        }

        ~Promise() {
            //printf("%lx freed\n", (uintptr_t)this);
        }

        template<typename OnFulfilled, typename OnRejected>
        promise_t then(function<OnFulfilled(pm_any_t)> on_fulfilled,
                      function<OnRejected(pm_any_t)> on_rejected) {
            switch (state)
            {
                case State::Pending:
                    return promise_t([this, on_fulfilled, on_rejected](promise_t npm) {
                        add_on_fulfilled(gen_on_fulfilled(on_fulfilled, npm));
                        add_on_rejected(gen_on_rejected(on_rejected, npm));
                    });
                case State::Fulfilled:
                    return promise_t([this, on_fulfilled](promise_t npm) {
                        gen_on_fulfilled(on_fulfilled, npm)();
                    });
                case State::Rejected:
                    return promise_t([this, on_rejected](promise_t npm) {
                        gen_on_rejected(on_rejected, npm)();
                    });
                default: PROMISE_ERR_INVALID_STATE;
            }
        }

        template<typename OnFulfilled>
        promise_t then(function<OnFulfilled(pm_any_t)> on_fulfilled) {
            switch (state)
            {
                case State::Pending:
                    return promise_t([this, on_fulfilled](promise_t npm) {
                        add_on_fulfilled(gen_on_fulfilled(on_fulfilled, npm));
                        add_on_rejected([this, npm]() {npm.reject(reason);});
                    });
                case State::Fulfilled:
                    return promise_t([this, on_fulfilled](promise_t npm) {
                        gen_on_fulfilled(on_fulfilled, npm)();
                    });
                case State::Rejected:
                    return promise_t([this](promise_t npm) {npm.reject(reason);});
                default: PROMISE_ERR_INVALID_STATE;
            }
        }
 
        template<typename OnRejected>
        promise_t fail(function<OnRejected(pm_any_t)> on_rejected) {
            switch (state)
            {
                case State::Pending: