[英]Unpack vector into arguments to call functions that have a variable number of arguments
[英]Succinctly rewrite a set of functions with variable number of arguments
我试图找出一种更干净的方式来编写此丑陋的代码:
class PythonExtensionBase : public PyObject
{
:
public:
// helper functions to call function fn_name with 0 to 9 args
Object callOnSelf( const std::string &fn_name );
Object callOnSelf( const std::string &fn_name, const Object &arg1 );
Object callOnSelf( const std::string &fn_name, const Object &arg1, const Object &arg2 );
Object callOnSelf( const std::string &fn_name, const Object &arg1, const Object &arg2, const Object &arg3 );
:
(一直到9)
这些功能在相应的.cxx中实现:
Py::Object PythonExtensionBase::callOnSelf( const std::string &fn_name )
{
Py::TupleN args;
return self().callMemberFunction( fn_name, args );
}
Py::Object PythonExtensionBase::callOnSelf( const std::string &fn_name,
const Py::Object &arg1 )
{
Py::TupleN args( arg1 );
return self().callMemberFunction( fn_name, args );
}
Py::Object PythonExtensionBase::callOnSelf( const std::string &fn_name,
const Py::Object &arg1, const Py::Object &arg2 )
{
Py::TupleN args( arg1, arg2 );
return self().callMemberFunction( fn_name, args );
}
Py::Object PythonExtensionBase::callOnSelf( const std::string &fn_name,
const Py::Object &arg1, const Py::Object &arg2, const Py::Object &arg3 )
{
Py::TupleN args( arg1, arg2, arg3 );
return self().callMemberFunction( fn_name, args );
}
:
有效的任务是概括:
X( A a, B b1, B b2, B b3 ) {
foo( b1, b2, b3 );
}
我可以看到可变参数模板可能是可行的方法,但是我正在努力了解如何使用它。
该TupleN类的定义如下:
class TupleN: public Tuple
{
public:
TupleN()
: Tuple( 0 )
{
}
TupleN( const Object &obj1 )
: Tuple( 1 )
{
setItem( 0, obj1 );
}
TupleN( const Object &obj1, const Object &obj2 )
: Tuple( 2 )
{
setItem( 0, obj1 );
setItem( 1, obj2 );
}
TupleN( const Object &obj1, const Object &obj2, const Object &obj3 )
: Tuple( 3 )
{
setItem( 0, obj1 );
setItem( 1, obj2 );
setItem( 2, obj3 );
}
:
virtual ~TupleN()
{ }
};
这是您使用可变参数模板的方式:
template <class... Arg>
Object callOnSelf( const std::string &fn_name, Arg&&... arg )
{
Py::TupleN args(std::forward<Arg>(arg)...);
return self().callMemberFunction( fn_name, args );
}
可变参数模板的问题在于您不能限制它们使用“ 特定类型的可变参数数量”。 您可以按原样保留它(并从TupleN
构造函数中获取编译错误),也可以使用静态断言和帮助器对其稍加帮助:
template <class Car, class... Cdr>
struct isObject
{
static constexpr bool value = isObject<Car> && isObject<Cdr...>::value;
};
template <class T>
struct isObject<T>
{
static constexpr bool value = std::is_convertible<const T&, const Py::Object&>::value;
};
template <class... Arg>
Object callOnSelf( const std::string &fn_name, Arg&&... arg )
{
static_assert(isObject<Arg...>::value, "All arguments to callOnSelf must be PyObject compatible");
Py::TupleN args(std::forward<Arg>(arg)...);
return self().callMemberFunction( fn_name, args );
}
至于TupleN
类,您可以执行类似的技巧:
class TupleN: public Tuple
{
public:
template <class Arg...>
TupleN(Arg&&... arg)
: Tuple( sizeof...(arg))
{
setItems(0, std::forward<Arg>(arg)...);
}
private:
template <class Car, class... Cdr>
void setItems(size_t idx, Car&& car, Cdr&&... cdr) {
setItem(idx, std::forward<Car>(car));
setItems(idx + 1, std::forward<Cdr>(cdr)...);
}
void setItems(size_t) // recursion terminator
{}
};
您想要的是可变参数模板参数。
template<class...Args>
Py::Object PythonExtensionBase::callOnSelf( const std::string &fn_name,
Args&&...args_ )
{
Py::TupleN args( std::forward<Args>(args_)... );
return self().callMemberFunction( fn_name, args );
}
更新:但为什么将args_作为右值引用传递?
答:完美转发。
考虑:
struct X { }; // an expensive to copy object
foo(X {}); // call with a temporary
假设foo放弃了它对其他内部函数的参数
void foo(X x) // copied
{
inner_foo(x);
}
并且inner_foo进一步将X传递给工作程序
void inner_foo(X x) // copied
{
really_inner_foo(x); // copied again
}
您想避免所有这些副本,对吗? X甚至可能是不可复制的类型。
在传递X的特定情况下,编写此代码的方式是:
void foo(X x) {
inner_foo(std::move(x));
}
并且可以提高效率(完全避免任何移动):
template<class X_LIKE>
void foo(X_LIKE&& x) {
inner_foo(std::forward<X_LIKE>(x));
}
因为如果需要,r值引用将绑定到l值引用,因此完全可以将const ref传递给X:
const X x;
foo(x);
foo然后有效地变为:
void foo(const X& x) {
inner_foo(x); // calls the const X& version of inner_foo
}
因此,在一般模板形式中,我们通过r值引用传递并使用std :: forward <>,因为此构造完美地保留了传递的内容。 如果您传递了参考,则会一直作为参考传递。 如果传递对象,则该对象将作为r值传递,直到使用它的最后时刻。
如果您想了解更多信息,请在Google上搜索“完美转发”,并准备让您大开眼界:-)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.