繁体   English   中英

简洁地用可变数量的参数重写一组函数

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM