C ++可变参数模板参数方法传递给没有可变参数的方法 [英] C++ variadic template arguments method to pass to a method without variadic arguments

查看:163
本文介绍了C ++可变参数模板参数方法传递给没有可变参数的方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下问题,我真的无法编译从所有的问题和文章研究:



在C ++中,是可能有一个方法与variadic指定参数类型的模板参数(作为in,out,in / out的参数的元描述类型,通过值,通过地址等),循环遍历这些可变参数,以便实例化指定类型的变量,并将这些变量传递给模板参数中的指针指定的函数,但这些函数没有可变参数?



EDIT 1



我尝试在这里详细说明伪代码:

  template< decltype(* Type :: * Method),typename ...参数> 
static bool ExecuteMethod(JSContext * cx,unsigned argc,JS :: Value * vp)
{
JS :: CallArgs args = CallArgsFromVp(argc,vp);

循环参数
{
Parameters [i] :: Type p [i] < - args [i]
}

返回类型r =方法(p [0],p [1],p [2] ... p [n]); //该方法没有可变参数
...
}

其中方法可能类似:

  int(* GetColor)(int16 * color) 
int(* GetFile)(FilePath& file);
int(* WriteDocument)(const FilePath& file,const char * fileFormatName,bool askForParms);

等。



包装需求。
挑战是C ++中缺少的东西,如.net中的反射。
有可能通过循环遍历可变参数来异构对象的数组。大概。
但是如何将它们传递给没有可变参数的方法?我认为不可能将这个对象数组分配给上面这三个没有显式包装器的函数,不是吗?



EDIT 2



我有很多反馈,但很明显我不够具体。
我没有详细说太多,因为我有过于太具体的抱怨。事实上,我没有简单的实现,我是一个通用的人,不是懒惰,但我试图使后面的开发更快。



这里是问题的根源:我需要包装Adobe Illustrator API,它暴露了数百个(如果不是数千个)指向structs,称为套件的函数的指针。



有一个使用SpiderMonkey的javascript引擎。



我使用Visual Studio 2015编译器。



我的方法如下:



我有几个类来封装API,以便为所有套件添加SpiderMonkey的引擎对象。每个SpiderMonkey类都可以调用为 jsData ,包装的数据类型为Adobe SDK或套件 jsSuite



到目前为止,我已经使用模板,因为SpiderMonkey强制我添加每个函数到其自定义对象与一个特定的签名,如下:

  bool jsAIDocumentSuite :: WriteDocument(JSContext * cx,unsigned argc,JS :: Value * vp)
{
...
}

并将其添加到自定义对象将会是这样:

  const JSFunctionSpec jsAIDocumentSuite :: fFunctions [] = {
...
JS_FN(WriteDocument,jsAIDocumentSuite :: WriteDocument,3,0),
。 。
}

JS_FN是一个SpiderMonkeyMacro。



实际上,到目前为止,这还不到Adobe SDK的10%。



最多的是带有一个参数的getter和setter,或地址或指针,所以我用一个通用函数替换它们,像这样:

  template< typename jsType,typename jsReturnType ,typename ReturnPrivateType = jsReturnType :: PrivateType,typename jsParamType,typename ParamPrivateType = jsParamType :: PrivateType,ReturnPrivateType(* Type :: * Method)(ParamPrivateType&)& 
static bool GetByRefMethod(JSContext * cx,unsigned argc,JS :: Value * vp)
{
JS :: CallArgs args = CallArgsFromVp(argc,vp);

try
{
ReturnPrivateType result;

ParamPrivateType ppt;

if(jsType :: Suite()&&(jsType :: Suite() - > * Method))
result =(jsType :: Suite *方法)(ppt);
else
return false; // TODO抛出一个有意义的错误

if((jsReturnType :: IsNoError(result))&&(argc> 0)&&(args [0] .isObject )
{
JSObject * obj =& args [0] .toObject();

JSObject * value = NULL;
if(!jsParamType :: FromAIObject< jsParamType>(cx,& ppt,value))
return false;

if(!value)
return false;

jsProperty :: SetProperty(cx,& obj,value,value,true);
}

JSObject * obj = JS_NewObject(cx,& jsDataClass< jsReturnType> :: fClass);

JS_SetPrivate(obj,new ReturnPrivateType(result));

args.rval()。setObject(* obj);
}
EXCEPTION_CATCH_CONVERT();

return true;
}

有点复杂,不是吗?



以上是相关的:




  • 它的引擎传递的SpiderMonkey参数

  • 此处只传递一个参数, ppt

  • 返回类型是一个值,因此很容易处理



我使用宏来注入方法的变体(几个简短的形式,这里):

  JS_FN(#GET_METHOD,(js ## TYPE :: GetByRefMethod< js ## TYPE,RETURN_JS_TYPE,RETURN_PRIVATE_TYPE, PARAM_JS_TYPE,PARAM_PRIVATE_TYPE,& TYPE :: GET_METHOD>),1,0)

能够处理可变参数,根据统计更加哲学,但有趣。



我希望如何:



<我希望添加可变参数元信息,如:



模板
static bool方法(JSContext * cx,unsigned argc,JS :: Value * vp)
{
JS :: CallArgs args = CallArgsFromVp(argc,vp);

  b $ b {
ReturnPrivateType result;

*第一个挑战:循环遍历元参数的可变参数列表,并在这里创建相应的对象实例,并使用SpiderMonkey引擎传递的* args *集合中的值初始化IN。*

if(jsType :: Suite()&&(jsType :: Suite() - > * Method))
result = (*第二个挑战:传递参数在这里:可能通过使用一个可变宏?
else
return false; // TODO抛出一个有意义的错误

if((jsReturnType :: IsNoError(result))&&(argc> 0)&&(args [0] .isObject )
{
JSObject * obj =& args [0] .toObject();

JSObject * value = NULL;
if(!jsParamType :: FromAIObject< jsParamType>(cx,& ppt,value))
return false;

if(!value)
return false;

jsProperty :: SetProperty(cx,& obj,value,value,true);
}

JSObject * obj = JS_NewObject(cx,& jsDataClass< jsReturnType> :: fClass);

JS_SetPrivate(obj,new ReturnPrivateType(result));

args.rval()。setObject(* obj);
}
EXCEPTION_CATCH_CONVERT();

return true;
}

正如你所看到的,它不是C ++所期望的,通过尝试避免写模板来减少参数,这里,我知道参数首先,并尝试写一个代码生成正确的参数,通过知道他们的元信息第一,我有一个明确的类型集合和I promise 来编写正确的代码以生成正确的包装。我不需要对参数的数据进行多少验证,因为在这个过程中,大多数情况下没有一个巨大的业务逻辑。



EDIT 3



关于参数元信息,我可以写一些带静态的类型来指定参数的数据类型,无论它是返回类型,它是一个IN,一个OUT或一个IN / OUT参数,它的 jsType 等。
它们将是上面模板参数函数的可变参数列表。

解决方案

[继续从第1部分: http:// stackoverflow。 com / a / 35109026/5386374 ]






但有一个问题。我们不得不改变我们的代码写入到 ExecuteMethod()的方式,这可能不总是可能的。有一个方法,使它的功能与您之前指定的 ExecuteMethod()完全相同,并且不需要将其修改为宏参数?答案是...是!

  //变量函数式宏来自动创建,使用和破坏函子。 
//取消注释适合所使用的编译器。
//(不同的是,如果
//宏具有零可变参数,Visual C ++会自动删除尾随逗号,而GCC需要以##的形式提示以告诉
//它这样做。)
//而不是do ... while结构,我们可以直接使用临时Executor。
// MSVC:
// #define ExecuteMethod(M,...)Executor< decltype(& M),decltype(& M)> {}(M,__VA_ARGS__)
// GCC:
#define ExecuteMethod(M,...)Executor< decltype(& M),decltype(& M)> {}(M,## __ VA_ARGS__)

//对于你的示例函数WriteDocument(),定义为
// int WriteDocument(const FilePath& file,const char * fileFormatName,bool askForParms);

bool c = ExecuteMethod(WriteDocument,file,fileFormatName,askForParams);

这很好,但我们可以做一个更改,性能。目前,这个函子只能接受函数指针(也许是lambdas,我不熟悉他们的语法),而不是其他类型的函数对象。如果这是想要的,这意味着我们可以重写它以去掉第一个模板参数(整个签名),因为第二和第三个参数本身是签名的组成部分。

  //默认函子。 
template< typename ... Ts>
struct Executor {};

//一般情况。
template< typename ReturnType,typename ... Params>
struct Executor< ReturnType(*)(Params ...)> {
private:
//而不是显式地将M作为参数,从
//其他参数创建它。
using M = ReturnType(*)(Params ...);
public:
//参数匹配:
bool operator()(M方法,Params ... params){
ReturnType r = method(params ...);
// ...
}

//参数不匹配:
模板< typename ... Invalid_Params>
bool operator()(M方法,Invalid_Params ... ts){
//这里处理参数类型不匹配。
}
};

//特殊情况下捕获void返回类型。
template< typename ... Params>
struct Executor< void(*)(Params ...)> {
private:
//不是显式地将M作为参数,而是从
//其他参数创建它。
using M = void(*)(Params ...);
public:
//参数匹配:
bool operator()(M method,Params ... params){
method(params ...);
// ...
}

//参数不匹配:
模板< typename ... Invalid_Params>
bool operator()(M方法,Invalid_Params ... ts){
//这里处理参数类型不匹配。
}
};

//变量函数式宏来自动创建,使用和破坏函子。
//取消注释适合所使用的编译器。
//(不同的是,如果
//宏具有零可变参数,Visual C ++会自动删除尾随逗号,而GCC需要以##的形式提示以告诉
//它这样做。)
//而不是do ... while结构,我们可以直接使用临时Executor。
// MSVC:
// #define ExecuteMethod(M,...)Executor< decltype(& M)> {}(M,__VA_ARGS__)
// GCC:
#define ExecuteMethod(M,...)Executor< decltype(& M)> {}(M,## __ VA_ARGS__)


//注意:编译器不支持C ++ 11using类型别名,用下面的代替
//:
// typedef ReturnType(* M)(Params ...);

这会导致更清晰的代码,但是,如上所述,限制函子只接受函数指针。 / p>

当这样使用时,函子期望参数是完全匹配的。它可以正确处理引用和cv,但可能有问题的右值,我不知道。请参阅此处



如何使用你的 JSContext ...我真的不知道。我还没有学习上下文,所以别人会更有帮助的。我建议检查,如果这里的其他答案是否会更有用,在所有诚实的情况下。






注意:我不知道如果它的函数参数是一个函子,lambda, std :: function ,或任何排序。






注意2:和以前一样,我不确定是否会对性能产生任何负面影响喜欢这个。可能有更有效的方法,但我不知道会是什么。


I have the following question, I really can't compile from all the questions and articles researched:

In C++, is it possible to have a method with variadic template arguments that specify types of arguments (as a meta-description type for parameters of in, out, in/out of a certain type, to be passed by value, by address etc.), to loop through these variadic arguments in order to instantiate variables of specified types, and be passed these variables to functions specified by a pointer in a template parameter, but these functions not having variadic parameters?

EDIT 1

I try here to detail, as pseudocode:

template <decltype(*Type::*Method), typename... Parameters>
static bool ExecuteMethod(JSContext *cx, unsigned argc, JS::Value *vp)
{
    JS::CallArgs args = CallArgsFromVp(argc, vp);

    loop through Parameters
    {
        Parameters[i]::Type p[i] <-- args[i];
    }

    ReturnType r = Method(p[0], p[1], p[2] .. p[n]); // the method does not have variadic parameters
...
}

where Method might be like:

int(*GetColor) ( int16 *color);
int(*GetFile) ( FilePath &file );
int(*WriteDocument) ( const FilePath &file, const char *fileFormatName, bool askForParms);

etc.

This comes out of wrapping needs. The challenge is something missing in C++, reflection as in .net. It is possible to instance an array of heterogeneous objects by looping through the variadic arguments somehow? Probably. But how pass them to methods having no variadic arguments? I think it is not possible to assign that array of objects to functions like these three above without explicit wrappers, isn't it?

EDIT 2

I've got a lot of feed-back, but it is clear I was not specific enough. I did not detailed too much because I've got complains in the past for being too specific. Indeed, I do not have easy implementations and I am a generic guy, not lazy, but I try to make a latter development faster.

Here is the source of the problem: I need to wrap Adobe Illustrator API, which exposes hundreds if not thousands of pointers to functions grouped in structs, called suites.

I try to have a javascript engine using SpiderMonkey.

I use Visual Studio 2015 compiler.

My approach is as follows:

I have several classes to wrap the API in order to add to SpiderMonkey's engine objects for all the suites. Each SpiderMonkey class, could be called as jsData, wraps a data type of Adobe SDK, or a suite, jsSuite.

So far, I have used templates because SpiderMonkey forces me to add each function to its custom objects with a specific signature, like this:

bool jsAIDocumentSuite::WriteDocument(JSContext *cx, unsigned argc, JS::Value *vp)
{
...
}

and adding it to the custom object would be done like this:

const JSFunctionSpec jsAIDocumentSuite::fFunctions[] = {
...
    JS_FN("WriteDocument", jsAIDocumentSuite::WriteDocument, 3, 0),
...
}

JS_FN is a SpiderMonkeyMacro.

Actually, this is, so far, less than 10% of the Adobe SDK.

The most are getters and setters with one parameter, passed by value or address or pointer, so I have replaced them by a generic function, like this:

    template <typename jsType, typename jsReturnType, typename ReturnPrivateType = jsReturnType::PrivateType, typename jsParamType, typename ParamPrivateType = jsParamType::PrivateType, ReturnPrivateType(*Type::*Method)(ParamPrivateType&)>
    static bool GetByRefMethod(JSContext *cx, unsigned argc, JS::Value *vp)
    {
        JS::CallArgs args = CallArgsFromVp(argc, vp);

        try
        {
            ReturnPrivateType result;

            ParamPrivateType ppt;

            if (jsType::Suite() && (jsType::Suite()->*Method))
                result = (jsType::Suite()->*Method)(ppt);
            else
                return false; // TODO throw a meaningful error

            if ((jsReturnType::IsNoError(result)) && (argc > 0) && (args[0].isObject()))
            {
                JSObject *obj = &args[0].toObject();

                JSObject *value = NULL;
                if (!jsParamType::FromAIObject<jsParamType>(cx, &ppt, value))
                    return false;

                if (!value)
                    return false;

                jsProperty::SetProperty(cx, &obj, "value", value, true);
            }

            JSObject *obj = JS_NewObject(cx, &jsDataClass<jsReturnType>::fClass);

            JS_SetPrivate(obj, new ReturnPrivateType(result));

            args.rval().setObject(*obj);
        }
        EXCEPTION_CATCH_CONVERT();

        return true;
    }

A bit complicated, isn't it?

What is relevant, above, is:

  • The args variable holds the SpiderMonkey parameters passed in by its engine
  • Only one argument is passed here, ppt
  • The return type is one value, so it is easy to be handled

I use macros to inject the method in its variants (several short forms too, not so interesting here):

JS_FN(#GET_METHOD, (js##TYPE::GetByRefMethod<js##TYPE, RETURN_JS_TYPE, RETURN_PRIVATE_TYPE, PARAM_JS_TYPE, PARAM_PRIVATE_TYPE, &TYPE::GET_METHOD>), 1, 0)

I wish to be able to handle variable arguments, according to the statistics more philosophical, but interesting. The idea would be opposite to the C++, probably, and not as expected.

How would I expect it:

I wish to add variadic parameters meta-information, like:

template static bool Method(JSContext *cx, unsigned argc, JS::Value *vp) { JS::CallArgs args = CallArgsFromVp(argc, vp);

        try
        {
            ReturnPrivateType result;

            *1st challenge: Loop through the variadic list of meta-parameters and create their corresponding object instances here and initialize the IN ones with values from the *args* collection passed by the SpiderMonkey engine*

            if (jsType::Suite() && (jsType::Suite()->*Method))
                result = (jsType::Suite()->*Method)(*2nd challenge: pass arguments here: probably by using a variadic macro?*);
            else
                return false; // TODO throw a meaningful error

            if ((jsReturnType::IsNoError(result)) && (argc > 0) && (args[0].isObject()))
            {
                JSObject *obj = &args[0].toObject();

                JSObject *value = NULL;
                if (!jsParamType::FromAIObject<jsParamType>(cx, &ppt, value))
                    return false;

                if (!value)
                    return false;

                jsProperty::SetProperty(cx, &obj, "value", value, true);
            }

            JSObject *obj = JS_NewObject(cx, &jsDataClass<jsReturnType>::fClass);

            JS_SetPrivate(obj, new ReturnPrivateType(result));

            args.rval().setObject(*obj);
        }
        EXCEPTION_CATCH_CONVERT();

        return true;
    }

As you can see, it is not as C++ expected, it is a bit reversed, by trying to avoid writing templates to deduct the parameters, here, I know the parameters first and try to write a code to generate the right parameters by knowing their meta-information first and I have a clear set of types and I promise to write the right code to generate the correct wrappers. I don't need to validate much regarding the data of the parameters, as things are mostly passed without a huge business logic in the process.

EDIT 3

About the parameters meta-information, I could write a few types with statics to specify the data type of the parameter, whether it is a return type, whether it is an IN, an OUT or an IN/OUT parameter, its jsType etc.. They would be the variadic list of the template parameters function above.

解决方案

[Continued from part 1: http://stackoverflow.com/a/35109026/5386374 ]


There is an issue, however. We had to change the way our code is written to accomodate ExecuteMethod(), which may not always be possible. Is there a way around that, so that it functions exactly the same as your previously specified ExecuteMethod(), and doesn't need to take the variable it modifies as a macro parameter? The answer is... yes!

// Variadic function-like macro to automatically create, use, and destroy functor.
// Uncomment whichever one is appropriate for the compiler used.
//  (The difference being that Visual C++ automatically removes the trailing comma if the
//   macro has zero variadic arguments, while GCC needs a hint in the form of "##" to tell
//   it to do so.)
// Instead of a do...while structure, we can just use a temporary Executor directly.
// MSVC:
// #define ExecuteMethod(M, ...) Executor<decltype(&M), decltype(&M)>{}(M, __VA_ARGS__)
// GCC:
#define ExecuteMethod(M, ...) Executor<decltype(&M), decltype(&M)>{}(M, ##__VA_ARGS__)

// For your example function WriteDocument(), defined as
//   int WriteDocument(const FilePath &file, const char *fileFormatName, bool askForParms);

bool c = ExecuteMethod(WriteDocument, file, fileFormatName, askForParams);

This is all well and good, but there is one more change we can make to simplify things without impacting performance. At the moment, this functor can only take function pointers (and maybe lambdas, I'm not familiar with their syntax), not other types of function objects. If this is intended, it means that we can rewrite it to do away with the first template parameter (the entire signature), since the second and third parameters are themselves components of the signature.

// Default functor.
template<typename... Ts>
struct Executor { };

// General case.
template<typename ReturnType, typename... Params>
struct Executor<ReturnType (*)(Params...)> {
    private:
        // Instead of explicitly taking M as a parameter, create it from
        //  the other parameters.
        using M = ReturnType (*)(Params...);
    public:
        // Parameter match:
        bool operator()(M method, Params... params) {
            ReturnType r = method(params...);
            // ...
        }

        // Parameter mismatch:
        template<typename... Invalid_Params>
        bool operator()(M method, Invalid_Params... ts) {
            // Handle parameter type mismatch here.
        }
};

// Special case to catch void return type.
template<typename... Params>
struct Executor<void (*)(Params...)> {
    private:
        // Instead of explicitly taking M as a parameter, create it from
        //  the other parameters.
        using M = void (*)(Params...);
    public:
        // Parameter match:
        bool operator()(M method, Params... params) {
            method(params...);
            // ...
        }

        // Parameter mismatch:
        template<typename... Invalid_Params>
        bool operator()(M method, Invalid_Params... ts) {
            // Handle parameter type mismatch here.
        }
};

// Variadic function-like macro to automatically create, use, and destroy functor.
// Uncomment whichever one is appropriate for the compiler used.
//  (The difference being that Visual C++ automatically removes the trailing comma if the
//   macro has zero variadic arguments, while GCC needs a hint in the form of "##" to tell
//   it to do so.)
// Instead of a do...while structure, we can just use a temporary Executor directly.
// MSVC:
// #define ExecuteMethod(M, ...) Executor<decltype(&M)>{}(M, __VA_ARGS__)
// GCC:
#define ExecuteMethod(M, ...) Executor<decltype(&M)>{}(M, ##__VA_ARGS__)


// Note: If your compiler doesn't support C++11 "using" type aliases, replace them
//       with the following:
//           typedef ReturnType (*M)(Params...);

This results in cleaner code, but, as mentioned, limits the functor to only accepting function pointers.

When used like this, the functor expects parameters to be an exact match. It can handle reference-ness and cv-ness correctly, but may have issues with rvalues, I'm not sure. See here.

As to how to use this with your JSContext... I'm honestly not sure. I haven't learned about contexts yet, so someone else would be more helpful for that. I would suggest checking if one of the other answers here would be more useful in your situation, in all honesty.


Note: I'm not sure how easy it would be to modify the functor to work if its function parameter is a functor, lambda, std::function, or anything of the sort.


Note 2: As before, I'm not sure if there would be any negative effects on performance for doing something like this. There's likely a more efficient way, but I don't know what it would be.

这篇关于C ++可变参数模板参数方法传递给没有可变参数的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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