2014-10-06 1 views
2

Я пытаюсь выяснить более чистый способ написания этого довольно уродливый код:Сжато переписать набор функций с переменным числом аргументов

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() 
    { } 
}; 
+1

Я искренне надеюсь, что единственная причина, это не потому, что автор изначально не имел наличие возможностей C++ 11. Если это действительно так, и проблема с тех пор была решена, многие из них действительно могут быть пересмотрены для вариативного решения. – WhozCraig

+0

В качестве альтернативы варианту решения вы можете использовать 'std :: initializer_list '. – Jarod42

ответ

4

Вот как вы бы использовать VARIADIC шаблон:

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 
    {} 
}; 
+0

hah! Идентичные ответы в то же время :-) –

+0

@RichardHodges Почти. Убейте вас через минуту :-) – Angew

+0

Спасибо за оба ответа :) Как я могу убрать класс TupleN, который в настоящее время страдает от такого же уродливого дублирования? –

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_ как ссылку на rvalue?

ответ: безупречный отправка.

считают:

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, если это необходимо, и поэтому вполне допустимо передать константу 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 до самого последнего момента, когда он используется.

Если вы хотите узнать больше, сделать поиск Google для «идеального» экспедирования и подготовиться, чтобы ваш ум взорван :-)

+0

Я начинаю это видеть! Не могли бы вы объяснить double &&? –

+0

@Pi обновленный ответ. соберите меня, если хотите узнать больше. –