通过转换它们来组成可变参数模板参数
Compose variadic template argument by transforming them
我有一个简单的情况,可能需要复杂的方法才能解决,但我不确定。
基本上我有这个封装了成员函数的对象:
template<class T, typename R, typename... ARGS>
class MemberFunction
{
private:
using function_type = R (T::*)(ARGS...);
function_type function;
public:
MemberFunction(function_type function) : function(function) { }
void call(T* object, ARGS&&... args)
{
(object->*function)(args...);
}
};
这个用起来很方便
MemberFunction<Foo, int, int, int> function(&Foo::add)
Foo foo;
int res = function.call(&foo, 10,20)
问题是我想通过自定义环境来调用它,该环境使用一堆值来操作此方法,这转换为以下代码:
int arg2 = stack.pop().as<int>();
int arg1 = stack.pop().as<int>();
Foo* object = stack.pop().as<Foo*>();
int ret = function.call(object, arg1, arg2);
stack.push(Value(int));
这很容易直接在代码中完成,但我想找到一种方法将此行为直接封装到 MemberFunction
class 中,方法是公开一个 void call(Stack& stack)
方法我的工作是为了获得类似的东西:
MemberFunction<Foo, int, int, int> function(&Foo::add);
Stack stack;
stack.push(Value(new Foo());
stack.push(10);
stack.push(20);
function.call(stack);
assert(stack.pop().as<int>() == Foo{}.add(10,20));
但由于我是可变参数模板的新手,我不知道如何才能高效而优雅地完成。
编辑:添加有关 Stack 和 StackValue 的细节
我说的堆栈是 std::stack<StackValue>
的包装器,它提供了压入和弹出元素的模板方法,例如
struct StackValue
{
union
{
float fvalue;
s32 ivalue;
bool bvalue;
FloatPair fpair;
IntPair ipair;
void* ptr;
};
template<typename T> T as();
template<typename T> StackValue(T type);
StackValue() { }
};
template<> inline StackValue::StackValue(float f) : fvalue(f) { }
template<> inline StackValue::StackValue(s32 i) : ivalue(i) { }
...
template<> inline float StackValue::as<float>() { return fvalue; }
template<> inline s32 StackValue::as<s32>() { return ivalue; }
...
class Stack
{
private:
std::stack<StackValue> stack;
public:
StackValue& peek() { return stack.top(); }
StackValue pop() { StackValue v = stack.top(); stack.pop(); return v; }
void push(StackValue value) { stack.push(value); }
template<typename T> void pushValue(T value) { stack.push(StackValue(value)); }
template<typename T> T popValue() {
StackValue v = stack.top().as<T>();
stack.pop();
return v;
}
}
我们将不得不递归地执行此操作,以确保我们以正确的顺序弹出内容:
void call(Stack& s) {
call_impl(std::integral_constant<int, sizeof...(ARGS)>{}, s);
}
与:
template <int N, typename... StackVals>
void call_impl(std::integral_constant<int, N>, Stack& s, StackVals... vals) {
call_impl(std::integral_constant<int, N-1>{}, s, s.pop(), vals...);
}
template <typename... StackVals
void call_impl(std::integral_constant<int, 0>,
Stack& s,
StackVals... vals)
{
// now we have all the args
T* object = s.pop().as<T*>();
// so just call
s.push(call(object, vals.as<Args>()...));
}
首先,我们 pop
一个一个地提出所有论点,并且我们将下一个 pop()
-ed 放在前面。然后,关键表达式是:
vals.as<Args>()...
vals
是我们刚刚建立的StackValues
的参数包,Args
是函数参数的参数包。如果我们做对了,这两个包的大小应该相同(否则,将无法编译)。扩展将同时扩展为:
val0.as<Arg0>, val1.as<Arg1>, val2.as<Arg2>, ...
这正是我们要找的。我们唯一需要的另一件事是弹出 T*
,并将其作为 call()
.
的第一个参数呈现
旁注,这个签名:
void call(T* object, ARGS&&... args);
不正确。这需要一堆右值引用。您要么想要使用 ARGS...
,要么将其作为函数模板来转发引用。它也应该 return R
.
我有一个简单的情况,可能需要复杂的方法才能解决,但我不确定。
基本上我有这个封装了成员函数的对象:
template<class T, typename R, typename... ARGS>
class MemberFunction
{
private:
using function_type = R (T::*)(ARGS...);
function_type function;
public:
MemberFunction(function_type function) : function(function) { }
void call(T* object, ARGS&&... args)
{
(object->*function)(args...);
}
};
这个用起来很方便
MemberFunction<Foo, int, int, int> function(&Foo::add)
Foo foo;
int res = function.call(&foo, 10,20)
问题是我想通过自定义环境来调用它,该环境使用一堆值来操作此方法,这转换为以下代码:
int arg2 = stack.pop().as<int>();
int arg1 = stack.pop().as<int>();
Foo* object = stack.pop().as<Foo*>();
int ret = function.call(object, arg1, arg2);
stack.push(Value(int));
这很容易直接在代码中完成,但我想找到一种方法将此行为直接封装到 MemberFunction
class 中,方法是公开一个 void call(Stack& stack)
方法我的工作是为了获得类似的东西:
MemberFunction<Foo, int, int, int> function(&Foo::add);
Stack stack;
stack.push(Value(new Foo());
stack.push(10);
stack.push(20);
function.call(stack);
assert(stack.pop().as<int>() == Foo{}.add(10,20));
但由于我是可变参数模板的新手,我不知道如何才能高效而优雅地完成。
编辑:添加有关 Stack 和 StackValue 的细节
我说的堆栈是 std::stack<StackValue>
的包装器,它提供了压入和弹出元素的模板方法,例如
struct StackValue
{
union
{
float fvalue;
s32 ivalue;
bool bvalue;
FloatPair fpair;
IntPair ipair;
void* ptr;
};
template<typename T> T as();
template<typename T> StackValue(T type);
StackValue() { }
};
template<> inline StackValue::StackValue(float f) : fvalue(f) { }
template<> inline StackValue::StackValue(s32 i) : ivalue(i) { }
...
template<> inline float StackValue::as<float>() { return fvalue; }
template<> inline s32 StackValue::as<s32>() { return ivalue; }
...
class Stack
{
private:
std::stack<StackValue> stack;
public:
StackValue& peek() { return stack.top(); }
StackValue pop() { StackValue v = stack.top(); stack.pop(); return v; }
void push(StackValue value) { stack.push(value); }
template<typename T> void pushValue(T value) { stack.push(StackValue(value)); }
template<typename T> T popValue() {
StackValue v = stack.top().as<T>();
stack.pop();
return v;
}
}
我们将不得不递归地执行此操作,以确保我们以正确的顺序弹出内容:
void call(Stack& s) {
call_impl(std::integral_constant<int, sizeof...(ARGS)>{}, s);
}
与:
template <int N, typename... StackVals>
void call_impl(std::integral_constant<int, N>, Stack& s, StackVals... vals) {
call_impl(std::integral_constant<int, N-1>{}, s, s.pop(), vals...);
}
template <typename... StackVals
void call_impl(std::integral_constant<int, 0>,
Stack& s,
StackVals... vals)
{
// now we have all the args
T* object = s.pop().as<T*>();
// so just call
s.push(call(object, vals.as<Args>()...));
}
首先,我们 pop
一个一个地提出所有论点,并且我们将下一个 pop()
-ed 放在前面。然后,关键表达式是:
vals.as<Args>()...
vals
是我们刚刚建立的StackValues
的参数包,Args
是函数参数的参数包。如果我们做对了,这两个包的大小应该相同(否则,将无法编译)。扩展将同时扩展为:
val0.as<Arg0>, val1.as<Arg1>, val2.as<Arg2>, ...
这正是我们要找的。我们唯一需要的另一件事是弹出 T*
,并将其作为 call()
.
旁注,这个签名:
void call(T* object, ARGS&&... args);
不正确。这需要一堆右值引用。您要么想要使用 ARGS...
,要么将其作为函数模板来转发引用。它也应该 return R
.