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:
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.
I still am having some difficulty understanding exactly what you want to do, but this should let you call a function(without variardic parameters) using a variardic template function, getting the parameters from an array and allowing a conversion operation to apply to each parameter before being passed to the function:
#include <functional>
template<typename T, typename JST> T getParam(const JST& a)
{
//Do whatever conversion necessary
return a;
}
namespace detail
{
template<typename R, typename... Args, int... S> R jsCaller(std::function<R(Args...)> f, seq<S...>, const JS::CallArgs& args)
{
return f(getParam<Args, /*Whatever type should go here */>(args[S])...);
}
}
//Actually use this to call the function and get the result
template<typename R, typename... Args> R jsCall(std::function<R(Args...)> f, const JS::CallArgs& args)
{
return detail::jsCaller(f, GenSequence<sizeof...(Args)>(), args);
}
Where GenSequence extends seq<0, 1, 2, ... , N - 1> and can be implemented as follows:
template<int... N>
struct seq {};
template<int N, int... S>
struct gens : gens<N-1, N-1, S...> {};
template<int... S>
struct gens<0, S...>
{
typedef seq<S...> type;
};
template<int N> using GenSequence<N> = typename gens<N>::type;
This creates a parameter pack of integers, and expands the function call using them- See this question.
You can call your method using jsCall:
Result r = jsCall((Method), args);
Assuming Method can be converted to std::function- if not, you can still do it by making a lambda which conforms to std::function. Does this solve the problem?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With