我的情况很简单,可能需要采用复杂的方法来解决,但我不确定。
基本上,我有一个封装了成员函数的对象:
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
通过公开一个方法来将这种行为直接封装到类中,该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));
但是,由于我是可变参数模板的新手,所以我不知道如何有效而优雅地进行操作。
我正在谈论的堆栈是一个包装器,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()
到目前为止,我们将下一个论点放在其他论点之前。然后,关键表达式为:
vals.as<Args>()...
vals
是StackValues
我们刚刚建立Args
的参数包,并且是函数参数的参数包。如果我们做对了,这两个包的大小应该相同(否则,将无法编译)。扩展将同时扩展为:
val0.as<Arg0>, val1.as<Arg1>, val2.as<Arg2>, ...
这正是我们想要的。我们唯一需要做的另一件事就是弹出T*
,并将其作为的第一个参数提出call()
。
旁注,此签名:
void call(T* object, ARGS&&... args);
是不正确的。这需要大量右值引用。您可以ARGS...
将其用作功能模板或使其成为转发参考的功能模板。它也应该返回R
。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句