C ++函数调用包装器使用可变包装类型扩展 [英] C++ function caller wrapper using variadic pack types expansion

查看:228
本文介绍了C ++函数调用包装器使用可变包装类型扩展的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经研究了很多模板与可变包扩展,但仍然我无法编译所有的文章,我在这里找到为了实现我的目标。请接受我的道歉,因为,可能是多余的。
我绑定一些API,我绑定到一些函数签名,如下:

I have studied a lot templates with variadic pack expansion, but still I can't compile all the articles I've found here in order to achieve my goal. Please accept my apologies for being, probably, redundant. 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)



我尝试包装对象和函数在SpiderMonkey下的javascript中使用。
要集成一些C API,必须实现对象数据的封装器和某些对象的包装器方法。

I try to wrap objects and functions to use in javascript under SpiderMonkey. To integrate some C API, must be implemented wrappers for object data and wrapper methods for some object.

我的解决方案引导我的包装器,为了能够调用具有多个参数的方法,但是我不知道如何实现它:

My solution lead me to the following logic of the wrapper, in order to be able to call methods with several arguments, but I don't know how to achieve it:

    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();

这里开始我的问题。

strong>第一步。展开... jsParamType ... pack,为每个jsParamType调用一个方法,以便创建一个包装类对象实例,用于匹配args中的相应参数,以准备调用C API函数。

First step. Expand ...jsParamType... pack with calling a method for each jsParamType in order to create a wrapper class object instance for matching the corresponding argument out of args to prepare calling the C API function.

换句话说,jsParamType指示它包装的类型,因此它可以提取每个要传递给C API函数的参数的C类型对象。

In other words, jsParamType tells the type it wraps so it can extract the C type object for each parameter to be passed to the C API function.

第一个jsParamType对应于args [0],第二个jsParamType对应于args 1 等,到最后一个jsParamType,它对应于args [argc]。

The first jsParamType corresponds to the args[0], the second jsParamType to the args1 etc., to the last jsParamType, which corresponds the args[argc].

在args中可以获取比sizeof更少的元素。 .jsParamType,在这种情况下,基本C对象应该用一个默认值初始化。

It is possible to get less elements in args than sizeof...jsParamType, in this case the base C object should be initialized with a default value.

参数或对象包装器的元信息是用静态方法jsParamType :: jsType :: PrivateTypeDefaultValue())。

The meta-information of parameters or object wrappers is achieved with static methods already (jsParamType::jsType::PrivateTypeDefaultValue() for example).

最终,扩展包应该是异构对象的数组或向量。

Eventually, the expanded pack should be an array or vector of heterogeneous objects.

匹配函数应该基于jsParamType进行模板化,但还要获得扩展的可变参数包和args局部变量的索引,以便获得正确的对象进行解析 - 这里是我的第一个问题:

The matching function should be templated, based on jsParamType, but also get the index of the expanded variadic pack and the args local variable in order to get the correct object to parse - here is my first issue:

如何将索引传递给方法?

How to pass the index to the method?

我试图从这里获得一些灵感,但我无法使其正常工作:为每个可变参数模板参数和数组调用函数

I've tried to have some inspiration out of here, but I can't make it work: Calling a function for each variadic template argument and an array

第二步。在这之后,我计划有一个类似的技术,这里:使用Variadic模板打开参数列表获取参数索引,以便使用正确的参数调用C API函数 - 这是否可能?

Second step. After this, I plan to have a similar technique to here: Obtain Argument Index while Unpacking Argument List with Variadic Templates in order to call the C API function with the right arguments - is this possible?

第三步。最后,基于jsParamType的静态函数,称为IsOut(),out值将更新args局部变量的内容,但是应该使用新的扩展,类似于第一步,重新放回一些

Third step. In the end, based on a static function of jsParamType, called IsOut(), the out values will update the content of args local variable, but this should be done again using a new expansion, similar to the first step, to put back some values using type info present in jsParamType elements.

最后要做的是设置返回值,这很简单。

The last thing to do would be to set the return value, which is trivial.

图例: jsType 是一种包含C API函数指针的结构类型,因为它们由C API的提供程序分组。
jsReturnType 为返回类型。 (仅供参考...)
方法是要调用的C API方法,具有 MethodType 类型。

Legend: jsType is a type of the struct containing the pointer to the C API functions, as these are grouped by the C API's provider. jsReturnType is the return type. (just for reference here...) Method is a C API method to be called, having the MethodType type.

请帮我写这样的代码,因为我不能得到它的工作。我不要求我为我和我的私人编码。我只是没有经验的这部分预处理器,我看到很多的例子,许多抛出错误,或者我不能扩展他们为了实现上述功能。非常感谢。

Please help me to write such code, as I can't get it work. I don't ask to think for me and be my private coders. I just have no experience with this part of the preprocessor, I see lots of examples, many throw errors, or I can't extend them in order to achieve the above function. Thank you.

编辑1

感谢您的回覆, =http://stackoverflow.com/users/27678/andygtitle =AndyG> AndyG 。这里是或多或少的伪代码:

Thanks for the reply, AndyG. Here is the more or less pseudo-code:

    // *jsType* is a type of the struct containing the pointer to the C API functions, as these are grouped by the C API's provider.
    // *jsReturnType* is the return type. (just for reference here...)
    // *Method* is a C API method to be called, having the *MethodType* type.
    // The non-template part of the function signature is needed untouched by SpiderMonkey

    template<typename jsType, typename jsReturnType, typename MethodType, MethodType Method, typename... jsParamType>
static bool VAMethodRet(JSContext *cx, unsigned argc, JS::Value *vp)
{
    // get the collection of passed arguments (SpiderMonkey stuff), in args, each element accessible by index
    // argc is the number of parameters passed from javascript
    WrappersCollection args = GetCallArgsFromVp(argc, vp);

    jsReturnType::PrivateType result = jsReturnType::PrivateTypeDefaultValue(); // creates an instance of a return value wrapper to receive the return value later

    // 1. Match wrappers in the WrappersCollection against their types in order to get their C type values
    CDataCollection cdata = array or vector of heterogeneous objects (int, char*, structs etc.);
    for each (jsParamType in the variadic pack ...)
    {
         cdata[jsParamTypeIndex] = Match<jsParamType, jsParamTypeIndex>(args);
    }
    // the above should be done by expanding the pack somehow
    // with emphasis on checking if argc[jsParamTypeIndex] was passed actually, because it's possible to have jsParamTypeIndex > argc
    // I don't know how to do it otherwise, by matching some param types variadic pack against a variadic pack of data arguments with different number of elements

    // 2. Transform the cdata collection into a variadic parameters pack in order to pass them to the C function.
    // The amount of args is specified by the function
    result = CFunction(cdata...);

    // 3. Update returned data in *out* params in the collection of wrapper objects received in the first place
    // index of the param in pack is necessary again
    // due to the different number of elements in the param types variadic pack versus the variadic pack of data arguments
    for each (jsParamType in the variadic pack ...)
    {
         if (jsParamType::IsOUT())
         {
              cdata[jsParamTypeIndex] = MatchBack<jsParamType, jsParamTypeIndex>(args);
         }
    }

    // the rest of things to do
    MatchReturnValue(result);

    return true; // SpiderMonkey needs to know the success of the operation
}


Is this more comprehensible enough?

推荐答案

感谢 AndyG ,我实现了我的目标。这里是一个代码示例,注意不提供实际包装器,因为它们是具体情况。因此,只需传递参数即可模拟它们。

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;
}

这篇关于C ++函数调用包装器使用可变包装类型扩展的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆