Skip to content

Instantly share code, notes, and snippets.

@Hackerl
Created April 3, 2022 06:30
Show Gist options
  • Save Hackerl/ff4e65fabab35a25c5804ce33a44e887 to your computer and use it in GitHub Desktop.
Save Hackerl/ff4e65fabab35a25c5804ce33a44e887 to your computer and use it in GitHub Desktop.

Revisions

  1. Hackerl created this gist Apr 3, 2022.
    596 changes: 596 additions & 0 deletions promise.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,596 @@
    #include <memory>
    #include <functional>
    #include <list>

    namespace zero {
    namespace promise {
    template<typename Result, typename Reason>
    class Promise;

    template<typename T>
    struct promise_traits {
    typedef T result_type;
    };

    template<typename Result, typename Reason>
    struct promise_traits<std::shared_ptr<Promise<Result, Reason>>> {
    typedef Result result_type;
    typedef Reason reason_type;
    };

    template<typename...Ts>
    using promises_result_t = decltype(std::tuple_cat(
    std::declval<
    typename std::conditional<
    std::is_same<void, typename promise_traits<Ts>::result_type>::value,
    std::tuple<>,
    std::tuple<typename promise_traits<Ts>::result_type>>::type
    >()...)
    );

    template<typename T>
    struct function_traits : public function_traits<decltype(&T::operator())> {

    };

    template<typename T, typename Result, typename Reason, typename... Args>
    struct function_traits<std::shared_ptr<Promise<Result, Reason>>(T::*)(Args...) const> {
    typedef Result result_type;
    typedef std::function<std::shared_ptr<Promise<Result, Reason>>(
    const std::remove_const_t<std::remove_reference_t<Args>> &...)> prototype;
    };

    template<typename T, typename ReturnType, typename... Args>
    struct function_traits<ReturnType(T::*)(Args...) const> {
    typedef ReturnType result_type;
    typedef std::function<ReturnType(
    const std::remove_const_t<std::remove_reference_t<Args>> &...)> prototype;
    };

    template<typename T>
    struct Wrapper {
    Wrapper() = default;

    explicit Wrapper(T v) : value(std::move(v)) {

    }

    T value{};
    };

    template<>
    struct Wrapper<void> {

    };

    template<typename T>
    struct wrapper_traits {
    typedef T type;
    };

    template<typename T>
    struct wrapper_traits<Wrapper<T>> {
    typedef T type;
    };

    template<typename Result, typename Argument, typename T, std::enable_if_t<std::is_same<std::remove_const_t<std::remove_reference_t<Argument>>, T>::value> * = nullptr>
    auto invoke(const std::function<Result(Argument)> &func, const Wrapper<T> &argument) {
    return func(argument.value);
    }

    template<typename Result, typename Argument, typename T, std::enable_if_t<std::is_same<std::remove_const_t<std::remove_reference_t<Argument>>, T>::value> * = nullptr>
    auto invoke(const std::function<Result(const Wrapper<Argument> &)> &func, const Wrapper<T> &argument) {
    return func(argument);
    }

    template<typename Result, typename Argument, typename T, std::enable_if_t<!std::is_same<typename wrapper_traits<std::remove_const_t<std::remove_reference_t<Argument>>>::type, T>::value> * = nullptr>
    auto invoke(const std::function<Result(Argument)> &func, const Wrapper<T> &argument) {
    return func({});
    }

    template<typename Result, typename T>
    auto invoke(const std::function<Result()> &func, const Wrapper<T> &argument) {
    return func();
    }

    template<typename P, typename Result, typename Reason, typename... Args, typename T, std::enable_if_t<!std::is_same<Result, void>::value> * = nullptr>
    auto trigger(const P &p, const std::function<std::shared_ptr<Promise<Result, Reason>>(Args...)> &func,
    const Wrapper<T> &argument) {
    invoke(func, argument)->then([=](const Result &result) {
    p->resolve(result);
    }, [=](const Reason &reason) {
    p->reject(reason);
    });
    }

    template<typename P, typename Result, typename... Args, typename T, std::enable_if_t<std::is_same<Result, void>::value> * = nullptr>
    auto trigger(const P &p, const std::function<Result(Args...)> &func, const Wrapper<T> &argument) {
    invoke(func, argument);
    p->resolve();
    }

    template<typename P, typename Result, typename... Args, typename T, std::enable_if_t<!std::is_same<Result, void>::value> * = nullptr>
    auto trigger(const P &p, const std::function<Result(Args...)> &func, const Wrapper<T> &argument) {
    p->resolve(Wrapper<Result>{invoke(func, argument)});
    }

    enum emState {
    PENDING,
    FULFILLED,
    REJECTED
    };

    struct CReason {
    int code;
    std::string message;
    std::shared_ptr<CReason> previous;
    };

    template<typename Result, typename Reason>
    class Promise : public std::enable_shared_from_this<Promise<Result, Reason>> {
    public:
    void start(const std::function<void(std::shared_ptr<Promise<Result, Reason>>)> &func) {
    func(this->shared_from_this());
    }

    public:
    void resolve(const Wrapper<Result> &result = {}) {
    if (mStatus != PENDING)
    return;

    mStatus = FULFILLED;
    mResult = result;

    for (const auto &n: mTriggers) {
    std::get<0>(n)();
    }
    }

    void reject(const Wrapper<Reason> &reason = {}) {
    if (mStatus != PENDING)
    return;

    mStatus = REJECTED;
    mReason = reason;

    for (const auto &n: mTriggers) {
    std::get<1>(n)();
    }
    }

    public:
    template<typename... Args>
    void resolve(Args... args) {
    resolve(Wrapper<Result>{{args...}});
    }

    template<typename... Args>
    void reject(Args... args) {
    reject(Wrapper<Reason>{{args...}});
    }

    public:
    template<typename Next, typename... ResultArgs, typename... ReasonArgs>
    auto then(const std::function<Next(ResultArgs...)> &onFulfilled,
    const std::function<Next(ReasonArgs...)> &onRejected) {
    auto p = std::make_shared<Promise<typename promise_traits<Next>::result_type, Reason>>();

    auto onFulfilledTrigger = [=]() {
    if (!onFulfilled) {
    invoke(
    std::function<void(const Wrapper<typename promise_traits<Next>::result_type> &)>(
    [p](auto &&result) {
    p->resolve(std::forward<decltype(result)>(result));
    }
    ),
    this->mResult
    );
    return;
    }

    trigger(p, onFulfilled, this->mResult);
    };

    auto onRejectedTrigger = [=]() {
    if (!onRejected) {
    invoke(
    std::function<void(const Wrapper<typename promise_traits<Reason>::result_type> &)>(
    [p](auto &&reason) {
    p->reject(std::forward<decltype(reason)>(reason));
    }
    ),
    this->mReason
    );
    return;
    }

    trigger(p, onRejected, this->mReason);
    };

    switch (mStatus) {
    case FULFILLED:
    onFulfilledTrigger();
    break;

    case REJECTED:
    onRejectedTrigger();
    break;

    case PENDING:
    mTriggers.push_back({onFulfilledTrigger, onRejectedTrigger});
    }

    return p;
    }

    auto finally(const std::function<void()> &onFinally) {
    auto p = std::make_shared<Promise<Result, Reason>>();

    auto onFulfilledTrigger = [=]() {
    onFinally();
    p->resolve(this->mResult);
    };

    auto onRejectedTrigger = [=]() {
    onFinally();
    p->reject(this->mReason);
    };

    switch (mStatus) {
    case FULFILLED:
    onFulfilledTrigger();
    break;

    case REJECTED:
    onRejectedTrigger();
    break;

    case PENDING:
    mTriggers.push_back({onFulfilledTrigger, onRejectedTrigger});
    }

    return p;
    }

    public:
    template<typename F>
    auto then(const F &onFulfilled) {
    return then((typename function_traits<F>::prototype) onFulfilled, {});
    }

    template<typename F1, typename F2>
    auto then(const F1 &onFulfilled, const F2 &onRejected) {
    return then((typename function_traits<F1>::prototype) onFulfilled,
    (typename function_traits<F2>::prototype) onRejected);
    }

    template<typename F>
    auto fail(const F &onRejected) {
    return then({}, (typename function_traits<F>::prototype) onRejected);
    }

    template<typename F>
    auto finally(const F &onFinally) {
    return finally((typename function_traits<F>::prototype) onFinally);
    }

    public:
    auto status() {
    return mStatus;
    }

    template<typename T = Result, std::enable_if_t<!std::is_same<T, void>::value> * = nullptr>
    auto value() {
    return mResult.value;
    }

    template<typename T = Reason, std::enable_if_t<!std::is_same<T, void>::value> * = nullptr>
    auto reason() {
    return mReason.value;
    }

    private:
    Wrapper<Result> mResult{};
    Wrapper<Reason> mReason{};

    private:
    emState mStatus{PENDING};
    std::list<std::tuple<std::function<void()>, std::function<void()>>> mTriggers;
    };

    template<typename Result>
    using CPromise = Promise<Result, CReason>;

    template<typename Result>
    auto chain(std::function<void(std::shared_ptr<CPromise<Result>>)> func) {
    auto p = std::make_shared<CPromise<Result>>();
    p->start(func);

    return p;
    }

    template<size_t index, typename P, typename R, typename Result, std::enable_if_t<!std::is_same<Result, void>::value> * = nullptr>
    void allForEach(
    const P &p,
    const R &results,
    const std::shared_ptr<size_t> &remain,
    const std::shared_ptr<CPromise<Result>> &current) {
    current->then([=](const Result &result) {
    std::get<index>(*results) = result;

    if (--(*remain) > 0)
    return;

    p->resolve(*results);
    }, [=](const CReason &reason) {
    p->reject(reason);
    });
    }

    template<size_t index, typename P, typename R, typename Result, std::enable_if_t<std::is_same<Result, void>::value> * = nullptr>
    void allForEach(
    const P &p,
    const R &results,
    const std::shared_ptr<size_t> &remain,
    const std::shared_ptr<CPromise<Result>> &current) {
    current->then([=]() {
    if (--(*remain) > 0)
    return;

    p->resolve(*results);
    }, [=](const CReason &reason) {
    p->reject(reason);
    });
    }

    template<size_t index, typename P, typename R, typename Result, typename... Args, std::enable_if_t<!std::is_same<Result, void>::value> * = nullptr>
    void allForEach(
    const P &p,
    const R &results,
    const std::shared_ptr<size_t> &remain,
    const std::shared_ptr<CPromise<Result>> &current,
    Args... args) {
    current->then([=](const Result &result) {
    std::get<index>(*results) = result;

    if (--(*remain) > 0)
    return;

    p->resolve(*results);
    }, [=](const CReason &reason) {
    p->reject(reason);
    });

    allForEach<index + 1>(p, results, remain, args...);
    }

    template<size_t index, typename P, typename R, typename Result, typename... Args, std::enable_if_t<std::is_same<Result, void>::value> * = nullptr>
    void allForEach(
    const P &p,
    const R &results,
    const std::shared_ptr<size_t> &remain,
    const std::shared_ptr<CPromise<Result>> &current,
    Args... args) {
    current->then([=]() {
    if (--(*remain) > 0)
    return;

    p->resolve(*results);
    }, [=](const CReason &reason) {
    p->reject(reason);
    });

    allForEach<index>(p, results, remain, args...);
    }

    template<typename Result, typename... Args>
    auto all(const std::shared_ptr<CPromise<Result>> &first, Args... args) {
    auto remain = std::make_shared<size_t>(sizeof...(Args) + 1);
    auto results = std::make_shared<promises_result_t<Result, Args...>>();

    return chain<promises_result_t<Result, Args...>>([=](auto p) {
    allForEach<0>(p, results, remain, first, args...);
    });
    }

    template<size_t index, typename P, typename R, typename Result>
    void allSettledForEach(
    const P &p,
    const R &results,
    const std::shared_ptr<size_t> &remain,
    const std::shared_ptr<CPromise<Result>> &current) {
    current->finally([=]() {
    std::get<index>(*results) = current;

    if (--(*remain) > 0)
    return;

    p->resolve(*results);
    });
    }

    template<size_t index, typename P, typename R, typename Result, typename... Args>
    void allSettledForEach(
    const P &p,
    const R &results,
    const std::shared_ptr<size_t> &remain,
    const std::shared_ptr<CPromise<Result>> &current,
    Args... args) {
    current->finally([=]() {
    std::get<index>(*results) = current;

    if (--(*remain) > 0)
    return;

    p->resolve(*results);
    });

    allSettledForEach<index + 1>(p, results, remain, args...);
    }

    template<typename Result, typename... Args>
    auto allSettled(const std::shared_ptr<CPromise<Result>> &first, Args... args) {
    auto remain = std::make_shared<size_t>(sizeof...(Args) + 1);
    auto results = std::make_shared<std::tuple<std::shared_ptr<CPromise<Result>>, Args...>>();

    return chain<std::tuple<std::shared_ptr<CPromise<Result>>, Args...>>([=](auto p) {
    allSettledForEach<0>(p, results, remain, first, args...);
    });
    }

    template<typename... Args>
    auto any(const std::shared_ptr<CPromise<void>> &first, Args... args) {
    return chain<void>([=](auto p) {
    auto remain = std::make_shared<size_t>(sizeof...(Args) + 1);
    auto tail = std::make_shared<CReason>();

    for (const auto &i: {first, args...}) {
    i->then([=]() {
    p->resolve();
    }, [=](const CReason &reason) {
    CReason last = reason;

    if (*remain != sizeof...(Args) + 1)
    last.previous = std::make_shared<CReason>(*tail);

    *tail = last;

    if (--(*remain) > 0)
    return;

    p->reject(*tail);
    });
    }
    });
    }

    template<typename Result, typename... Args>
    auto any(const std::shared_ptr<CPromise<Result>> &first, Args... args) {
    return chain<Result>([=](auto p) {
    auto remain = std::make_shared<size_t>(sizeof...(Args) + 1);
    auto tail = std::make_shared<CReason>();

    for (const auto &i: {first, args...}) {
    i->then([=](const Result &result) {
    p->resolve(result);
    }, [=](const CReason &reason) {
    CReason last = reason;

    if (*remain != sizeof...(Args) + 1)
    last.previous = std::make_shared<CReason>(*tail);

    *tail = last;

    if (--(*remain) > 0)
    return;

    p->reject(*tail);
    });
    }
    });
    }

    template<typename... Args>
    auto race(const std::shared_ptr<CPromise<void>> &first, Args... args) {
    return chain<void>([=](auto p) {
    for (const auto &i: {first, args...}) {
    i->then([=]() {
    p->resolve();
    }, [=](const CReason &reason) {
    p->reject(reason);
    });
    }
    });
    }

    template<typename Result, typename... Args>
    auto race(const std::shared_ptr<CPromise<Result>> &first, Args... args) {
    return chain<Result>([=](auto p) {
    for (const auto &i: {first, args...}) {
    i->then([=](const Result &result) {
    p->resolve(result);
    }, [=](const CReason &reason) {
    p->reject(reason);
    });
    }
    });
    }

    template<typename Result, typename... Args>
    auto reject(Args... args) {
    auto p = std::make_shared<CPromise<Result>>();
    p->reject(args...);

    return p;
    }

    template<typename Result, typename... Args>
    auto resolve(Args... args) {
    auto p = std::make_shared<CPromise<Result>>();
    p->resolve(args...);

    return p;
    }
    }
    }

    void testAll() {
    auto a = zero::promise::resolve<int>(1);
    auto b = zero::promise::resolve<void>();
    auto c = zero::promise::resolve<std::string>("hello");

    zero::promise::all(a, b, c)->then([](std::tuple<int, std::string> results) {
    printf("results: %d %s\n", std::get<0>(results), std::get<1>(results).c_str());
    });
    }

    void testAllSettled() {
    auto a = zero::promise::resolve<int>(1);
    auto b = zero::promise::reject<void>(1, "error");
    auto c = zero::promise::resolve<std::string>("hello");

    zero::promise::allSettled(a, b, c)->then([](const std::tuple<decltype(a), decltype(b), decltype(c)>& results) {
    printf("status: %d %d %d\n", std::get<0>(results)->status(), std::get<1>(results)->status(), std::get<2>(results)->status());
    });
    }

    void testAny() {
    auto a = zero::promise::resolve<int>(1);
    auto b = zero::promise::reject<int>(1, "error");
    auto c = zero::promise::resolve<int>(2);

    zero::promise::any(a, b, c)->then([](int result) {
    printf("result: %d\n", result);
    });

    auto d = zero::promise::reject<int>(2, "error 1");
    auto e = zero::promise::reject<int>(1, "error 2");

    zero::promise::any(d, e)->fail([](const zero::promise::CReason &reason) {
    printf("last reason: %d %s\n", reason.code, reason.message.c_str());
    });
    }

    void testRace() {
    auto a = zero::promise::resolve<int>(22);
    auto b = zero::promise::reject<int>(2, "error 2");
    auto c = zero::promise::resolve<int>(3);

    zero::promise::race(a, b, c)->then([](int result) {
    printf("result: %d\n", result);
    });

    auto d = zero::promise::reject<int>(2, "error 2");
    auto e = zero::promise::resolve<int>(1);

    zero::promise::race(d, e)->fail([](const zero::promise::CReason &reason) {
    printf("reason: %d %s\n", reason.code, reason.message.c_str());
    });
    }

    int main() {
    testAll();
    testAllSettled();
    testAny();
    testRace();
    }