使用可变参数包类型扩展的C ++函数调用包装器

问题描述 投票:1回答:1

我绑定了一些API,我绑定了一些函数签名,如下所示:

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

我尝试将对象和函数包装在SpiderMonkey下的javascript中使用。

要集成一些C API,必须实现对象数据的包装器和某些对象的包装器方法。

我的解决方案引导我使用包装器的以下逻辑,以便能够使用多个参数调用方法,但我不知道如何实现它:

    template<typename jsType, typename jsReturnType, typename MethodType, MethodType Method, typename... jsParamType>
static bool VAMethodRet(JSContext *cx, unsigned argc, JS::Value *vp)
{
    JS::CallArgs args = CallArgsFromVp(argc, vp);

    jsReturnType::PrivateType result = jsReturnType::PrivateTypeDefaultValue();

我的问题在这里开始:

  1. 展开... jsParamType ... pack为每个jsParamType调用一个方法,以便创建一个包装类对象实例,用于匹配args中的相应参数以准备调用C API函数。 换句话说,jsParamType告诉它包装的类型,因此它可以为每个要传递给C API函数的参数提取C类型对象。 第一个jsParamType对应于args[0],第二个jsParamType对应于args[1],等等到最后一个jsParamType,它对应于args[argc]。 args中的元素可能少于sizeof...jsParamType,在这种情况下,基本C对象应使用默认值初始化。 参数或对象包装器的元信息已经通过静态方法实现(例如jsParamType::jsType::PrivateTypeDefaultValue())。 最终,扩展包应该是异构对象的数组或向量。 匹配函数应该基于jsParamType进行模板化,还要获取扩展的可变参数包和args局部变量的索引,以便获得正确的解析对象 - 这是我的第一个问题: 如何将索引传递给方法? 我试图从here受到启发,但我不能让它发挥作用。

2 ..在此之后,我计划使用与here类似的技术,以便用正确的参数调用C API函数 - 这可能吗?

  1. 最后,基于jsParamType的静态函数,称为IsOut(),out值将更新args局部变量的内容,但这应该使用类似于第一步的新扩展再次使用,以使用类型信息存在于jsParamType元素中。

最后要做的是设置返回值,这是微不足道的。

c++ wrapper variadic-templates
1个回答
1
投票

感谢AndyG的帮助和耐心,我实现了我的目标。下面是一个代码示例,其中注释未提供实际的包装器,因为它们具体情况因具体情况而异。因此,只需传递参数即可模拟它们。

#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;
}
© www.soinside.com 2019 - 2024. All rights reserved.