问题
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)
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();
Here my issues begin:
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.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.The first
jsParamType
corresponds to theargs[0]
, the secondjsParamType
to theargs[1]
, etc. to the lastjsParamType
, which corresponds theargs[argc]
.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.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.
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 be inspired from here, but I can't make it work.
2.. After this, I plan to have a similar technique to here in order to call the C API function with the right arguments - is this possible?
- In the end, based on a static function of
jsParamType
, calledIsOut()
, the out values will update the content ofargs
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 injsParamType
elements.
The last thing to do would be to set the return value, which is trivial.
回答1:
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;
}
来源:https://stackoverflow.com/questions/36461452/c-function-caller-wrapper-using-variadic-pack-types-expansion