C++ function caller wrapper using variadic pack types expansion

后端 未结 1 1539
再見小時候
再見小時候 2021-01-24 21:01

I am bound to some APIs and I am tied to some function signatures like here:

static bool WrapperFunction(JSContext *cx, unsigned argc, JS::Value *vp)


        
相关标签:
1条回答
  • 2021-01-24 21:45

    Thanks to the help and patience of AndyG, I have achieved my goal. Here is a sample of code with the note that actual wrappers are not provided, as they are specific from case to case. So, they are simulated by simply passing parameters.

    #include <iostream>
    #include <functional>
    #include <tuple>
    #include <type_traits>
    #include <vector>
    using namespace std;
    
    #include <functional>
    #include <tuple>
    #include <type_traits>
    using namespace std;
    
    template<typename T, typename U, std::enable_if_t<std::is_same<T, U>::value, int> = 0>
    T convert_type(U _in)
    {
        //return const_cast<U>(_in);
        return _in;
    }
    
    template<typename T, typename U, std::enable_if_t<std::is_same<T, std::add_const_t<U>>::value, int> = 0>
    T convert_type(U _in)
    {
        //return const_cast<U>(_in);
        return _in;
    }
    
    
    // these conversion functions only can convert type to pointer to type, else return reference to type, so they're a bit limited
    // pointer to pointer, or
    template<typename T, typename U, std::enable_if_t<std::is_same<T, std::add_const_t<U>>::value, int> = 0>
    T& convert_type(U& _in)
    {
        return _in;
    }
    
    template<typename T, typename U, std::enable_if_t<std::is_same<T, std::add_lvalue_reference_t<U>>::value, int> = 0>
    T& convert_type(U& _in)
    {
        return _in;
    }
    
    template<typename T, typename U, std::enable_if_t<std::is_same<T, std::add_lvalue_reference_t<std::add_const_t<U>>>::value, int> = 0>
    T& convert_type(U& _in)
    {
        return _in;
    }
    
    
    // for conversion to pointer
    //T&* to T*
    template<typename T, typename U, std::enable_if_t<std::is_same<T, std::add_pointer_t<U>>::value, int> = 0>
    T convert_type(U& _in)
    {
        return std::addressof(_in);
    }
    
    template<typename T, typename U, std::enable_if_t<std::is_same<T, std::add_const_t<U>>::value, int> = 0>
    T convert_type(U& _in)
    {
        return std::addressof(_in);
    }
    
    template<typename T, typename U, std::enable_if_t<std::is_same<T, std::add_pointer_t<std::add_const_t<U>>>::value, int> = 0>
    T convert_type(U& _in)
    {
        return std::addressof(_in);
    }
    
    template<typename T>
    struct function_traits;
    
    template<typename R, typename ...Args>
    struct function_traits<std::function<R(Args...)>>
    {
        static const size_t nargs = sizeof...(Args);
    
        typedef R result_type;
    
        template <size_t i>
        struct arg
        {
            typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
        };
    
        static const bool isGlobalOrStaticContainer = true;
        static const bool isClassContainer = false;
        static const bool isPointerContainer = false;
        static const bool isConstInClassContainer = false;
        static const bool returnsVoid = std::is_same<R, void>::value;
    };
    
    template<typename C, typename R, typename ...Args>
    struct function_traits<std::function<R(*C::*)(Args...)>>
    {
        static const size_t nargs = sizeof...(Args);
    
        typedef R result_type;
    
        template <size_t i>
        struct arg
        {
            typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
        };
    
        static const bool isGlobalOrStaticContainer = false;
        static const bool isClassContainer = false;
        static const bool isPointerContainer = true;
        static const bool isConstInClassContainer = false;
        static const bool returnsVoid = std::is_same<R, void>::value;
    };
    
    template<typename C, typename R, typename ...Args>
    struct function_traits<std::function<R(C::*)(Args...)>>
    {
        static const size_t nargs = sizeof...(Args);
    
        typedef R result_type;
    
        template <size_t i>
        struct arg
        {
            typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
        };
    
        static const bool isGlobalOrStaticContainer = false;
        static const bool isClassContainer = true;
        static const bool isPointerContainer = false;
        static const bool isConstInClassContainer = false;
        static const bool returnsVoid = std::is_same<R, void>::value;
    };
    
    template<typename C, typename R, typename ...Args>
    struct function_traits<std::function<R(C::*)(Args...) const>>
    {
        static const size_t nargs = sizeof...(Args);
    
        typedef R result_type;
    
        template <size_t i>
        struct arg
        {
            typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
        };
    
        static const bool isGlobalOrStaticContainer = false;
        static const bool isClassContainer = true;
        static const bool isPointerContainer = false;
        static const bool isConstInClassContainer = true;
        static const bool returnsVoid = std::is_same<R, void>::value;
    };
    
    template<typename ParamType> class Param
    {
    public:
    
        typedef ParamType Type;
    
        static const bool isOut = false;
    };
    
    template<typename ParamType> class ParamOut : public Param<ParamType>
    {
    public:
    
        static const bool isOut = true;
    };
    
    template<typename Type, typename ReturnType, typename MethodType, MethodType Method, typename ParamType, size_t paramIndex, typename... ParamTypes>
    static bool UnwrapParameter(unsigned argc, std::vector<void*>& args, typename ParamType::Type &ppt)
    {
        if (argc > paramIndex)
        {
            ppt = *((std::add_pointer_t<typename ParamType::Type>(args[paramIndex])));
        }
    
        return true;
    }
    
    template<typename Type, typename ReturnType, typename MethodType, MethodType Method, typename... ParamType, size_t... paramIndex>
    static bool UnwrapParameters(unsigned argc, std::vector<void*>& args, std::tuple<typename ParamType::Type...>& params, std::index_sequence<paramIndex...>)
    {
        bool r[] = { true, UnwrapParameter<Type, ReturnType, MethodType, Method, ParamType, paramIndex, ParamType...>(argc, args, std::get<paramIndex>(params))... };
    
        bool res = true;
        for (size_t i = 0; i < sizeof...(ParamType) + 1 && res == true; i++)
            res &= r[i];
        return res;
    }
    
    template<typename Type, typename ReturnType, typename MethodType, MethodType Method, typename... ParamType>
    static bool UnwrapParameters(unsigned argc, std::vector<void*>& args, std::tuple<typename ParamType::Type...>& params)
    {
        return UnwrapParameters<Type, ReturnType, MethodType, Method, ParamType...>(argc, args, params, std::make_index_sequence<sizeof...(ParamType)>{});
    }
    
    
    template<typename Type, typename ReturnType, typename MethodType, MethodType Method, typename ParamType, size_t paramIndex, typename... ParamTypes>
    static bool WrapParameter(unsigned argc, std::vector<void*>& args, typename ParamType::Type &ppt)
    {
        if (ParamType::isOut && (argc > paramIndex))
        {
            // Wrap them back - nothing to do here, in this example
        }
    
        return true;
    }
    
    template<typename Type, typename ReturnType, typename MethodType, MethodType Method, typename... ParamType, size_t... paramIndex>
    static bool WrapParameters(unsigned argc, std::vector<void*>& args, std::tuple<typename ParamType::Type...>& params, std::index_sequence<paramIndex...>)
    {
        bool r[] = { true, WrapParameter<Type, ReturnType, MethodType, Method, ParamType, paramIndex, ParamType...>(argc, args, std::get<paramIndex>(params))... };
    
        bool res = true;
        for (size_t i = 0; i < sizeof...(ParamType)+1 && res == true; i++)
            res &= r[i];
        return res;
    }
    
    template<typename Type, typename ReturnType, typename MethodType, MethodType Method, typename... ParamType>
    static bool WrapParameters(unsigned argc, std::vector<void*>& args, std::tuple<typename ParamType::Type...>& params)
    {
        return WrapParameters<Type, ReturnType, MethodType, Method, ParamType...>(argc, args, params, std::make_index_sequence<sizeof...(ParamType)>{});
    }
    
    
    template<typename Type, typename ReturnType, typename MethodType,
        typename std::enable_if<function_traits<std::function<typename std::remove_pointer<MethodType>::type>>::isPointerContainer, MethodType>::type Method,
        typename... ParamType, size_t... paramIndex>
        static ReturnType CallMethodRet(bool& success, Type* obj, std::tuple<typename ParamType::Type...>& params, std::index_sequence<paramIndex...>)
    {
        if (!(obj && (obj->*Method)))
            success = false;
    
        return (obj->*Method)(convert_type<typename function_traits<std::function<typename std::remove_pointer<MethodType>::type>>::template arg<paramIndex>::type, typename ParamType::Type>(std::get<paramIndex>(params))...);
    }
    
    template<typename Type, typename ReturnType, typename MethodType,
        typename std::enable_if<function_traits<std::function<typename std::remove_pointer<MethodType>::type>>::isGlobalOrStaticContainer, MethodType>::type Method,
        typename... ParamType, size_t... paramIndex>
        static ReturnType CallMethodRet(bool& success, Type* obj, std::tuple<typename ParamType::Type...>& params, std::index_sequence<paramIndex...>)
    {
        if (!(*Method))
            success = false;
    
        return (*Method)(convert_type<typename function_traits<std::function<typename std::remove_pointer<MethodType>::type>>::template arg<paramIndex>::type, typename ParamType::Type>(std::get<paramIndex>(params))...);
    }
    
    template<typename Type, typename ReturnType, typename MethodType,
        typename std::enable_if<function_traits<std::function<typename std::remove_pointer<MethodType>::type>>::isClassContainer, MethodType>::type Method,
        typename... ParamType, size_t... paramIndex>
        static ReturnType CallMethodRet(bool& success, Type* obj, std::tuple<typename ParamType::Type...>& params, std::index_sequence<paramIndex...>)
    {
        if (!(obj && (Method)))
            success = false;
    
        return (obj->*Method)(convert_type<typename function_traits<std::function<typename std::remove_pointer<MethodType>::type>>::template arg<paramIndex>::type, typename ParamType::Type>(std::get<paramIndex>(params))...);
    }
    
    template <typename Type, typename ReturnType, typename MethodType, MethodType Method, typename... ParamType>
    static ReturnType CallMethodRet(bool& success, Type* obj, std::tuple<typename ParamType::Type...>& params)
    {
        return CallMethodRet<Type, ReturnType, MethodType, Method, ParamType...>(success, obj, params, std::make_index_sequence<sizeof...(ParamType)>{});
    }
    
    
    template<typename Type, typename ReturnType, typename MethodType,
        typename std::enable_if<!function_traits<std::function<typename std::remove_pointer<MethodType>::type>>::returnsVoid, MethodType>::type Method,
        typename... ParamType>
    static bool ExecuteMethod(Type* obj, unsigned argc, std::vector<void*>& args, ReturnType& result)
    {
        try
        {
            const unsigned numArgs = sizeof...(ParamType);
    
            std::tuple<typename ParamType::Type...> params = std::make_tuple(typename ParamType::Type()...);
    
            if (!UnwrapParameters<Type, ReturnType, MethodType, Method, ParamType...>(argc, args, params))
                return false;
    
            bool success = true;
    
            result = CallMethodRet<Type, ReturnType, MethodType, Method, ParamType...>(success, obj, params);
    
            if (!success)
               return false; // Throw method not found here
    
            if (!WrapParameters<Type, ReturnType, MethodType, Method, ParamType...>(argc, args, params))
                return false;
        }
        catch (...)
        {
            // whatever...
        }
    
        return true;
    }
    
    template<typename Type, typename ReturnType, typename MethodType,
        typename std::enable_if<function_traits<std::function<typename std::remove_pointer<MethodType>::type>>::returnsVoid, MethodType>::type Method,
        typename... ParamType>
        static bool ExecuteMethod(Type* obj, unsigned argc, std::vector<void*>& args)
    {
        try
        {
            const unsigned numArgs = sizeof...(ParamType);
    
            std::tuple<typename ParamType::Type...> params = std::make_tuple(typename ParamType::Type()...);
    
            if (!UnwrapParameters<Type, ReturnType, MethodType, Method, ParamType...>(argc, args, params))
                return false;
    
            bool success = true;
    
            CallMethodRet<Type, ReturnType, MethodType, Method, ParamType...>(success, obj, params);
    
            if (!success)
                return false; // Throw method not found here
    
            if (!WrapParameters<Type, ReturnType, MethodType, Method, ParamType...>(argc, args, params))
                return false;
        }
        catch (...)
        {
            // whatever...
        }
        return true;
    }
    
    class O 
    {
    public:
        void func(int a, string b, bool& c, const char* d)
        {
            std::cout << "Successfully called func with in values " << a << "," << b << "," << c << " and " << d << std::endl;
    
            c = true;
    
            std::cout << "Successfully called func with out values " << a << "," << b << "," << c << " and " << d << std::endl;
        }
    
        int func_i(int a, string b, bool& c, const char* d)
        {
            std::cout << "Successfully called func with in values " << a << "," << b << "," << c << " and " << d << std::endl;
    
            c = false;
    
            std::cout << "Successfully called func with out values " << a << "," << b << "," << c << " and " << d << std::endl;
    
            return 1;
        }
    };
    
    int main() {
    
        int a = 1;
        string b = "string";
        bool c = false;
        const char* d = "char*";
    
        std::vector<void*> v {(void*)&a, (void*)&b, (void*)&c, (void*)&d};
    
        std::cout << std::endl;
    
        O o;
    
        std::cout << ExecuteMethod<O, void, void(O::*)(int, string, bool&, const char*), &O::func, Param<int>, Param<string>, ParamOut<bool>, Param<const char*>>(&o, v.size(), v);
    
        std::cout << std::endl << std::endl;
    
        int result = 0;
        std::cout << ExecuteMethod<O, int, int(O::*)(int, string, bool&, const char*), &O::func_i, Param<int>, Param<string>, ParamOut<bool>, Param<const char*>>(&o, v.size(), v, result) << std::endl;
        std::cout << result << std::endl;
    
        return 0;
    }
    
    0 讨论(0)
提交回复
热议问题