aboutsummaryrefslogblamecommitdiff
path: root/promise.hpp
blob: 593d5c1f20cfc64a08461656ad2bde76570e439c (plain) (tree)
1
2
3


                      






















                                                                                 
                

                 
                     
                      
 
                          






                                     
      
      
 
                          






                                                                          
                   
                         
                              



                                                                                   

                                     




                                                                                       

                                             
 



                                                            



                                          
                              


                               




                                                   
                                   
      
 
                                 


                                                          

                                      


                                                                          

                                


                                                                          
 









                                                                   









                                                                   
                  



                                                        
               
                       

                                                                                  
 
                           
                            
                                                                




                                              

                                                      













                                                 

                         














                                          



                                                                 

                                    
 

                                                                
 


                                                               
 

                                                              





                                                                                                             

                                                                                  

                                                    
                               


                                             

                          
                               


                         





                        

                                                         

         

                                                        
         
 



                                                                       
                                                                        
                                
                                

                                                                    











                                                  


              



                                                                  
                                                               
                                
                          

                                                                    











                                                  


              


                                                                                  
                                                                                      




                                                                                
                                                                                     






                                                                                  

                                                                                
                                     
                                






                                                                                  
                                                                                     
                               
                                






                                                                                

                                                                              
                                    
                               






                                                                                

                                                                              
                              
                               






                                                                                  

                                                                                
                                                    






                                                                                  
                                                                                     
                                              






                                                                                
                                                                                         
                                                  


              



                                                                                
                                                                                   



                                            
                               





















                                                                     
              
















                                                    


                                




















































                                                                     









                                                           
      

               

                                           
 


                                                               


                                    

                                                                                         

                                                                                     

                                                                                     
                               


                                      
                   
                                      
                                       

                                                                                        

                                                                     
                                     
                                       

                                                                                     

                                                                   



                                                   
                                        
                                                      


                                    
                                       

                                                                                        
                                                                                     
                                                                           
                               


                                      
                   
                                      
                                       

                                                                                        

                                                                     
                                     
                                                                                 



                                                   
                                       
                                                    


                                    
                                       

                                                                                     

                                                                                  
                                                                             
                               


                                      
                   
                                      
                                                                                  
                                     
                                       

                                                                                     

                                                                   


                                                   








                                                           
                                        
                                        
             

                                  



                                       
                                        
             

                                 



             
                                                                       
                                                           

                                                                      
                                                  
                                   

                                                
                         


                                                                
                                                    
                      
                                                                    
                               


                                      




                      
                                                                        
                                                           
                                                

                                                                         
                               


                                      



             






                                               



                                  
                                    







                                   
                        
                                                                                

                        
                                                                              
 


                                                                  


                                                               


                                                                                  

      
                           
                                                                  



                                                                  
                                                                

                                                          
                                                                    

          
 
                           



                                                                  
                                                                       


                           

                                                                
                                                                             
 
                           




                                                                  
                                                                





                                                                  



                                                                  
                                                                


                                                               
                                                                    

          
 




                                                                 
                                                      


                                                  
 

                                                                        
                                                                                          

     


                                                                     

                                                                                         

     

                                                                      
                                                                                        

     
 
      
#ifndef _CPPROMISE_HPP
#define _CPPROMISE_HPP

/**
 * MIT License
 * Copyright (c) 2018 Ted Yin <[email protected]>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <stack>
#include <vector>
#include <memory>
#include <functional>
#include <type_traits>

#if __cplusplus >= 201703L
#ifdef __has_include
#   if __has_include(<any>)
#       include <any>
#       ifdef __cpp_lib_any
#           define _CPPROMISE_STD_ANY
#       endif
#   endif
#endif
#endif

#ifndef _CPPROMISE_STD_ANY
#include <boost/any.hpp>
#endif

/**
 * Implement type-safe Promise primitives similar to the ones specified by
 * Javascript Promise/A+.
 */
namespace promise {
#ifdef _CPPROMISE_STD_ANY
    using pm_any_t = std::any;
    template<typename T>
    constexpr auto any_cast = static_cast<T(*)(const std::any&)>(std::any_cast<T>);
    using bad_any_cast = std::bad_any_cast;
#else
#   warning "using boost::any"
#   pragma message "using boost::any"
    using pm_any_t = boost::any;
    template<typename T>
    constexpr auto any_cast = static_cast<T(*)(const boost::any&)>(boost::any_cast<T>);
    using bad_any_cast = boost::bad_any_cast;
#endif
    using callback_t = std::function<void()>;
    using values_t = std::vector<pm_any_t>;

    /* match lambdas */
    template<typename T>
    struct function_traits:
        public function_traits<decltype(&T::operator())> {};
 
    template<typename ReturnType>
    struct function_traits<ReturnType()> {
        using ret_type = ReturnType;
        using arg_type = void;
        using empty_arg = void;
    };
   
    /* match plain functions */
    template<typename ReturnType, typename ArgType>
    struct function_traits<ReturnType(ArgType)> {
        using ret_type = ReturnType;
        using arg_type = ArgType;
        using non_empty_arg = void;
    };
 
    /* match function pointers */
    template<typename ReturnType, typename... ArgType>
    struct function_traits<ReturnType(*)(ArgType...)>:
        public function_traits<ReturnType(ArgType...)> {};

    /* match const member functions */
    template<typename ClassType, typename ReturnType, typename... ArgType>
    struct function_traits<ReturnType(ClassType::*)(ArgType...) const>:
        public function_traits<ReturnType(ArgType...)> {};

    /* match member functions */
    template<typename ClassType, typename ReturnType, typename... ArgType>
    struct function_traits<ReturnType(ClassType::*)(ArgType...)>:
        public function_traits<ReturnType(ArgType...)> {};

    template<typename Func, typename ReturnType>
    using enable_if_return = typename std::enable_if<
            std::is_same<typename function_traits<Func>::ret_type,
                         ReturnType>::value>;

    template<typename Func, typename ReturnType>
    using disable_if_return = typename std::enable_if<
            !std::is_same<typename function_traits<Func>::ret_type,
                         ReturnType>::value>;

    template<typename Func, typename ArgType>
    using enable_if_arg = typename std::enable_if<
            std::is_same<typename function_traits<Func>::arg_type,
                         ArgType>::value>;

    template<typename Func, typename ArgType>
    using disable_if_arg = typename std::enable_if<
            !std::is_same<typename function_traits<Func>::arg_type,
                         ArgType>::value>;

    class Promise;
    //class promise_t: public std::shared_ptr<Promise> {
    class promise_t {
        Promise *pm;
        size_t *ref_cnt;
        public:
        friend Promise;
        template<typename PList> friend promise_t all(const PList &promise_list);
        template<typename PList> friend promise_t race(const PList &promise_list);

        inline promise_t();
        inline ~promise_t();
        template<typename Func> inline promise_t(Func callback);

        void swap(promise_t &other) {
            std::swap(pm, other.pm);
            std::swap(ref_cnt, other.ref_cnt);
        }

        promise_t &operator=(const promise_t &other) {
            if (this != &other)
            {
                promise_t tmp(other);
                tmp.swap(*this);
            }
            return *this;
        }

        promise_t &operator=(promise_t &&other) {
            if (this != &other)
            {
                promise_t tmp(std::move(other));
                tmp.swap(*this);
            }
            return *this;
        }

        promise_t(const promise_t &other):
            pm(other.pm),
            ref_cnt(other.ref_cnt) {
            ++*ref_cnt;
        }

        promise_t(promise_t &&other):
            pm(other.pm),
            ref_cnt(other.ref_cnt) {
            other.pm = nullptr;
        }

        Promise *operator->() const {
            return pm;
        }

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

        template<typename FuncFulfilled>
        inline promise_t then(FuncFulfilled on_fulfilled) const;

        template<typename FuncFulfilled, typename FuncRejected>
        inline promise_t then(FuncFulfilled on_fulfilled,
                            FuncRejected on_rejected) const;

        template<typename FuncRejected>
        inline promise_t fail(FuncRejected on_rejected) 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 {
        template<typename PList> friend promise_t all(const PList &promise_list);
        template<typename PList> friend promise_t race(const PList &promise_list);
        std::vector<callback_t> fulfilled_callbacks;
        std::vector<callback_t> rejected_callbacks;
#ifdef CPPROMISE_USE_STACK_FREE
        std::vector<Promise *> fulfilled_pms;
        std::vector<Promise *> rejected_pms;
#endif
        enum class State {
            Pending,
#ifdef CPPROMISE_USE_STACK_FREE
            PreFulfilled,
            PreRejected,
#endif
            Fulfilled,
            Rejected,
        } state;
        pm_any_t result;
        pm_any_t reason;

        void add_on_fulfilled(callback_t &&cb) {
            fulfilled_callbacks.push_back(std::move(cb));
        }

        void add_on_rejected(callback_t &&cb) {
            rejected_callbacks.push_back(std::move(cb));
        }

        template<typename Func,
            typename function_traits<Func>::non_empty_arg * = nullptr>
        static constexpr auto cps_transform(
                Func f, const pm_any_t &result, const promise_t &npm) {
            return [&result, npm, f = std::forward<Func>(f)]() mutable {
#ifndef CPPROMISE_USE_STACK_FREE
                f(result)->then(
                    [npm] (pm_any_t result) {npm->resolve(result);},
                    [npm] (pm_any_t reason) {npm->reject(reason);});
#else
                promise_t rpm{f(result)};
                rpm->then(
                    [rpm, npm] (pm_any_t result) {
                        npm->_resolve(result);
                    },
                    [rpm, npm] (pm_any_t reason) {
                        npm->_reject(reason);
                    });
                rpm->_dep_resolve(npm);
                rpm->_dep_reject(npm);
#endif
            };
        }

        template<typename Func,
            typename function_traits<Func>::empty_arg * = nullptr>
        static constexpr auto cps_transform(
                Func f, const pm_any_t &, const promise_t &npm) {
            return [npm, f = std::forward<Func>(f)]() mutable {
#ifndef CPPROMISE_USE_STACK_FREE
                f()->then(
                    [npm] (pm_any_t result) {npm->resolve(result);},
                    [npm] (pm_any_t reason) {npm->reject(reason);});
#else
                promise_t rpm{f()};
                rpm->then(
                    [rpm, npm] (pm_any_t result) {
                        npm->_resolve(result);
                    },
                    [rpm, npm] (pm_any_t reason) {
                        npm->_reject(reason);
                    });
                rpm->_dep_resolve(npm);
                rpm->_dep_reject(npm);
#endif
            };
        }

        template<typename Func,
            typename enable_if_return<Func, promise_t>::type * = nullptr>
        constexpr auto gen_on_fulfilled(Func on_fulfilled, const promise_t &npm) {
            return cps_transform(std::forward<Func>(on_fulfilled), this->result, npm);
        }

        template<typename Func,
            typename enable_if_return<Func, promise_t>::type * = nullptr>
        constexpr auto gen_on_rejected(Func on_rejected, const promise_t &npm) {
            return cps_transform(std::forward<Func>(on_rejected), this->reason, npm);
        }


        template<typename Func,
            typename enable_if_return<Func, void>::type * = nullptr,
            typename function_traits<Func>::non_empty_arg * = nullptr>
        constexpr auto gen_on_fulfilled(Func on_fulfilled, const promise_t &npm) {
            return [this, npm,
                    on_fulfilled = std::forward<Func>(on_fulfilled)]() mutable {
                on_fulfilled(result);
                npm->_resolve();
            };
        }

        template<typename Func,
            typename enable_if_return<Func, void>::type * = nullptr,
            typename function_traits<Func>::empty_arg * = nullptr>
        constexpr auto gen_on_fulfilled(Func on_fulfilled, const promise_t &npm) {
            return [on_fulfilled = std::forward<Func>(on_fulfilled), npm]() mutable {
                on_fulfilled();
                npm->_resolve();
            };
        }

        template<typename Func,
            typename enable_if_return<Func, void>::type * = nullptr,
            typename function_traits<Func>::non_empty_arg * = nullptr>
        constexpr auto gen_on_rejected(Func on_rejected, const promise_t &npm) {
            return [this, npm,
                    on_rejected = std::forward<Func>(on_rejected)]() mutable {
                on_rejected(reason);
                npm->_reject();
            };
        }

        template<typename Func,
            typename enable_if_return<Func, void>::type * = nullptr,
            typename function_traits<Func>::empty_arg * = nullptr>
        constexpr auto gen_on_rejected(Func on_rejected, const promise_t &npm) {
            return [npm,
                    on_rejected = std::forward<Func>(on_rejected)]() mutable {
                on_rejected();
                npm->_reject();
            };
        }

        template<typename Func,
            typename enable_if_return<Func, pm_any_t>::type * = nullptr,
            typename function_traits<Func>::non_empty_arg * = nullptr>
        constexpr auto gen_on_fulfilled(Func on_fulfilled, const promise_t &npm) {
            return [this, npm,
                    on_fulfilled = std::forward<Func>(on_fulfilled)]() mutable {
                npm->_resolve(on_fulfilled(result));
            };
        }

        template<typename Func,
            typename enable_if_return<Func, pm_any_t>::type * = nullptr,
            typename function_traits<Func>::empty_arg * = nullptr>
        constexpr auto gen_on_fulfilled(Func on_fulfilled, const promise_t &npm) {
            return [npm, on_fulfilled = std::forward<Func>(on_fulfilled)]() mutable {
                npm->_resolve(on_fulfilled());
            };
        }

        template<typename Func,
            typename enable_if_return<Func, pm_any_t>::type * = nullptr,
            typename function_traits<Func>::non_empty_arg * = nullptr>
        constexpr auto gen_on_rejected(Func on_rejected, const promise_t &npm) {
            return [this, npm, on_rejected = std::forward<Func>(on_rejected)]() mutable {
                npm->_reject(on_rejected(reason));
            };
        }

        template<typename Func,
            typename enable_if_return<Func, pm_any_t>::type * = nullptr,
            typename function_traits<Func>::empty_arg * = nullptr>
        constexpr auto gen_on_rejected(Func on_rejected, const promise_t &npm) {
            return [npm, on_rejected = std::forward<Func>(on_rejected)]() mutable {
                npm->_reject(on_rejected());
            };
        }

#ifdef CPPROMISE_USE_STACK_FREE
        void _trigger() {
            std::stack<std::tuple<
                std::vector<Promise *>::const_iterator,
                std::vector<Promise *> *,
                Promise *>> s;
            auto push_frame = [&s](Promise *pm) {
                if (pm->state == State::PreFulfilled)
                {
                    pm->state = State::Fulfilled;
                    for (auto &cb: pm->fulfilled_callbacks) cb();
                    s.push(std::make_tuple(pm->fulfilled_pms.begin(),
                                          &pm->fulfilled_pms,
                                          pm));
                }
                else if (pm->state == State::PreRejected)
                {
                    pm->state = State::Rejected;
                    for (auto &cb: pm->rejected_callbacks) cb();
                    s.push(std::make_tuple(pm->rejected_pms.begin(),
                                          &pm->rejected_pms,
                                          pm));
                }
            };
            push_frame(this);
            while (!s.empty())
            {
                auto &u = s.top();
                auto &it = std::get<0>(u);
                auto vec = std::get<1>(u);
                auto pm = std::get<2>(u);
                if (it == vec->end())
                {
                    s.pop();
                    vec->clear();
                    pm->fulfilled_callbacks.clear();
                    pm->rejected_callbacks.clear();
                    continue;
                }
                push_frame(*it++);
            }
        }

        void trigger_fulfill() {
            state = State::PreFulfilled;
            _trigger();
        }

        void trigger_reject() {
            state = State::PreRejected;
            _trigger();
        }

        void _resolve() {
            if (state == State::Pending) state = State::PreFulfilled;
        }

        void _reject() {
            if (state == State::Pending) state = State::PreRejected;
        }

        void _dep_resolve(const promise_t &npm) {
            if (state == State::Pending)
                fulfilled_pms.push_back(npm.pm);
            else
                npm->_trigger();
        }

        void _dep_reject(const promise_t &npm) {
            if (state == State::Pending)
                rejected_pms.push_back(npm.pm);
            else
                npm->_trigger();
        }

        void _resolve(pm_any_t _result) {
            if (state == State::Pending)
            {
                result = _result;
                state = State::PreFulfilled;
            }
        }

        void _reject(pm_any_t _reason) {
            if (state == State::Pending)
            {
                reason = _reason;
                state = State::PreRejected;
            }
        }
#else
        void _resolve() { resolve(); }
        void _reject() { reject(); }
        void _resolve(pm_any_t result) { resolve(result); }
        void _reject(pm_any_t reason) { reject(reason); }

        void trigger_fulfill() {
            state = State::Fulfilled;
            for (const auto &cb: fulfilled_callbacks) cb();
            fulfilled_callbacks.clear();
        }

        void trigger_reject() {
            state = State::Rejected;
            for (const auto &cb: rejected_callbacks) cb();
            rejected_callbacks.clear();
        }
#endif
        public:

        Promise(): state(State::Pending) {}
        ~Promise() {}

        template<typename FuncFulfilled, typename FuncRejected>
        promise_t then(FuncFulfilled on_fulfilled,
                      FuncRejected on_rejected) {
            switch (state)
            {
                case State::Pending:
                return promise_t([this,
                                on_fulfilled = std::forward<FuncFulfilled>(on_fulfilled),
                                on_rejected = std::forward<FuncRejected>(on_rejected)
                                ](promise_t &npm) {
                    add_on_fulfilled(gen_on_fulfilled(std::move(on_fulfilled), npm));
                    add_on_rejected(gen_on_rejected(std::move(on_rejected), npm));
#ifdef CPPROMISE_USE_STACK_FREE
                    _dep_resolve(npm);
                    _dep_reject(npm);
#endif
                });
                case State::Fulfilled:
                return promise_t([this,
                                on_fulfilled = std::forward<FuncFulfilled>(on_fulfilled)
                                ](promise_t &npm) {
                    gen_on_fulfilled(std::move(on_fulfilled), npm)();
                });
                case State::Rejected:
                return promise_t([this,
                                on_rejected = std::forward<FuncRejected>(on_rejected)
                                ](promise_t &npm) {
                    gen_on_rejected(std::move(on_rejected), npm)();
                });
                default: PROMISE_ERR_INVALID_STATE;
            }
        }

        template<typename FuncFulfilled>
        promise_t then(FuncFulfilled &&on_fulfilled) {
            switch (state)
            {
                case State::Pending:
                return promise_t([this,
                                on_fulfilled = std::forward<FuncFulfilled>(on_fulfilled)
                                ](promise_t &npm) {
                    add_on_fulfilled(gen_on_fulfilled(std::move(on_fulfilled), npm));
                    add_on_rejected([this, npm]() {npm->_reject(reason);});
#ifdef CPPROMISE_USE_STACK_FREE
                    _dep_resolve(npm);
                    _dep_reject(npm);
#endif
                });
                case State::Fulfilled:
                return promise_t([this,
                                on_fulfilled = std::forward<FuncFulfilled>(on_fulfilled)
                                ](promise_t &npm) {
                    gen_on_fulfilled(std::move(on_fulfilled), npm)();
                });
                case State::Rejected:
                return promise_t([this](promise_t &npm) {npm->_reject(reason);});
                default: PROMISE_ERR_INVALID_STATE;
            }
        }
 
        template<typename FuncRejected>
        promise_t fail(FuncRejected &&on_rejected) {
            switch (state)
            {
                case State::Pending:
                return promise_t([this,
                                on_rejected = std::forward<FuncRejected>(on_rejected)
                                ](promise_t &npm) {
                    callback_t ret;
                    add_on_rejected(gen_on_rejected(std::move(on_rejected), npm));
                    add_on_fulfilled([this, npm]() {npm->_resolve(result);});
#ifdef CPPROMISE_USE_STACK_FREE
                    _dep_resolve(npm);
                    _dep_reject(npm);
#endif
                });
                case State::Fulfilled:
                return promise_t([this](promise_t &npm) {npm->_resolve(result);});
                case State::Rejected:
                return promise_t([this,
                                on_rejected = std::forward<FuncRejected>(on_rejected)
                                ](promise_t &npm) {
                    gen_on_rejected(std::move(on_rejected), npm)();
                });
                default: PROMISE_ERR_INVALID_STATE;
            }
        }
  
        void resolve() {
            if (state == State::Pending) trigger_fulfill();
        }

        void reject() {
            if (state == State::Pending) trigger_reject();
        }

        void resolve(pm_any_t _result) {
            if (state == State::Pending)
            {
                result = _result;
                trigger_fulfill();
            }
        }

        void reject(pm_any_t _reason) {
            if (state == State::Pending)
            {
                reason = _reason;
                trigger_reject();
            }
        }
    };
        
    template<typename PList> promise_t all(const PList &promise_list) {
        return promise_t([&promise_list] (promise_t &npm) {
            auto size = std::make_shared<size_t>(promise_list.size());
            auto results = std::make_shared<values_t>();
            if (!*size) PROMISE_ERR_MISMATCH_TYPE;
            results->resize(*size);
            size_t idx = 0;
            for (const auto &pm: promise_list) {
                pm->then(
                    [results, size, idx, npm](pm_any_t result) {
                        (*results)[idx] = result;
                        if (!--(*size))
                            npm->_resolve(*results);
                    },
                    [npm](pm_any_t reason) {npm->_reject(reason);});
#ifdef CPPROMISE_USE_STACK_FREE
                pm->_dep_resolve(npm);
                pm->_dep_reject(npm);
#endif
                idx++;
            }
        });
    }

    template<typename PList> promise_t race(const PList &promise_list) {
        return promise_t([&promise_list] (promise_t &npm) {
            for (const auto &pm: promise_list) {
                pm->then([npm](pm_any_t result) {npm->_resolve(result);},
                        [npm](pm_any_t reason) {npm->_reject(reason);});
#ifdef CPPROMISE_USE_STACK_FREE
                pm->_dep_resolve(npm);
                pm->_dep_reject(npm);
#endif
            }
        });
    }

    template<typename Func>
    inline promise_t::promise_t(Func callback):
            pm(new Promise()),
        ref_cnt(new size_t(1)) {
        callback(*this);
    }

    inline promise_t::promise_t():
        pm(new Promise()),
        ref_cnt(new size_t(1)) {}

    inline promise_t::~promise_t() {
        if (pm)
        {
            if (--*ref_cnt) return;
            delete pm;
            delete ref_cnt;
        }
    }

    template<typename T>
    inline void promise_t::resolve(T result) const { (*this)->resolve(result); }

    template<typename T>
    inline void promise_t::reject(T reason) const { (*this)->reject(reason); }

    inline void promise_t::resolve() const { (*this)->resolve(); }
    inline void promise_t::reject() const { (*this)->reject(); }

    template<typename T>
    struct callback_types {
        using arg_type = typename function_traits<T>::arg_type;
        using ret_type = typename std::conditional<
            std::is_same<typename function_traits<T>::ret_type, promise_t>::value,
            promise_t, pm_any_t>::type;
    };

    template<typename Func,
        typename disable_if_arg<Func, pm_any_t>::type * = nullptr,
        typename enable_if_return<Func, void>::type * = nullptr,
        typename function_traits<Func>::non_empty_arg * = nullptr>
    constexpr auto gen_any_callback(Func f) {
        using func_t = callback_types<Func>;
        return [f = std::forward<Func>(f)](pm_any_t v) mutable {
            try {
                f(any_cast<typename func_t::arg_type>(v));
            } catch (bad_any_cast &e) { PROMISE_ERR_MISMATCH_TYPE; }
        };
    }

    template<typename Func,
        typename enable_if_arg<Func, pm_any_t>::type * = nullptr,
        typename enable_if_return<Func, void>::type * = nullptr,
        typename function_traits<Func>::non_empty_arg * = nullptr>
    constexpr auto gen_any_callback(Func f) {
        return [f = std::forward<Func>(f)](pm_any_t v) mutable {f(v);};
    }

    template<typename Func,
        typename enable_if_return<Func, void>::type * = nullptr,
        typename function_traits<Func>::empty_arg * = nullptr>
    constexpr auto gen_any_callback(Func f) { return std::forward<Func>(f); }

    template<typename Func,
        typename enable_if_arg<Func, pm_any_t>::type * = nullptr,
        typename disable_if_return<Func, void>::type * = nullptr,
        typename function_traits<Func>::non_empty_arg * = nullptr>
    constexpr auto gen_any_callback(Func f) {
        using func_t = callback_types<Func>;
        return [f = std::forward<Func>(f)](pm_any_t v) mutable {
            return typename func_t::ret_type(f(v));
        };
    }

    template<typename Func,
        typename disable_if_arg<Func, pm_any_t>::type * = nullptr,
        typename disable_if_return<Func, void>::type * = nullptr,
        typename function_traits<Func>::non_empty_arg * = nullptr>
    constexpr auto gen_any_callback(Func f) {
        using func_t = callback_types<Func>;
        return [f = std::forward<Func>(f)](pm_any_t v) mutable {
            try {
                return typename func_t::ret_type(
                    f(any_cast<typename func_t::arg_type>(v)));
            } catch (bad_any_cast &e) { PROMISE_ERR_MISMATCH_TYPE; }
        };
    }

    template<typename Func,
        typename disable_if_return<Func, void>::type * = nullptr,
        typename function_traits<Func>::empty_arg * = nullptr>
    constexpr auto gen_any_callback(Func f) {
        using func_t = callback_types<Func>;
        return [f = std::forward<Func>(f)]() mutable {
            return typename func_t::ret_type(f());
        };
    }

    template<typename FuncFulfilled>
    inline promise_t promise_t::then(FuncFulfilled on_fulfilled) const {
        return (*this)->then(gen_any_callback(std::forward<FuncFulfilled>(on_fulfilled)));
    }

    template<typename FuncFulfilled, typename FuncRejected>
    inline promise_t promise_t::then(FuncFulfilled on_fulfilled,
                                    FuncRejected on_rejected) const {
        return (*this)->then(gen_any_callback(std::forward<FuncFulfilled>(on_fulfilled)),
                            gen_any_callback(std::forward<FuncRejected>(on_rejected)));
    }

    template<typename FuncRejected>
    inline promise_t promise_t::fail(FuncRejected on_rejected) const {
        return (*this)->fail(gen_any_callback(std::forward<FuncRejected>(on_rejected)));
    }
}

#endif