我正试图找出一种更清晰的方式来编写这个相当丑陋的代码:
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()
{ }
};
答案 0 :(得分:4)
以下是您如何使用可变参数模板:
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 );
}
可变参数模板的问题在于,您不能将它们限制为使用&#34;特定类型的可变数量的参数。&#34; 您可以将其保留原样(并从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
{}
};
答案 1 :(得分:2)
你想要的是一个可变参数模板参数。
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传递给worker
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&lt;&gt;因为这个结构完美地保留了传递的内容。如果您传递引用,它将作为引用一直传递。如果你传递一个对象,它将作为一个r值传递,直到它被使用的最后一刻。
如果您想了解更多信息,可以在谷歌搜索“完美转发”并准备好让您的思绪大打折扣: - )