Pycxx Variadic Templates

in /CXX/Python3/ExtensionTypeBase.hxx

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 );
        // <etc>

And corresponding implementations in /Src/Python3/cxx_extensions.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 );
}
// etc

This is very ugly because (1) it takes up a lot of space with duplication, and (2) it only supports up to 9 arguments.

The problem can be solved supporting an arbitrary number of arguments using Variadic Templates:

class PythonExtensionBase : public PyObject
    {
    public:
        :
        template <class... Arg>
        Object callOnSelf( const std::string &fn_name, Arg&&... arg )
        {
            TupleN args(std::forward<Arg>(arg)...);
            return  self().callMemberFunction( fn_name, args );
        }

This replaces the above declarations in the .hxx. Since it contains the definition, the definitions can also be removed from the .cxx.


Variadic Templates can also rescue TupleN (in CXX/Python3/Objects.hxx)

The current code again is limited to 9 elements:

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 );
        }

        : (up to 9)

Here is the fix:

class TupleN: public Tuple
    {
    public:
        template <class ... Types> TupleN(Types&& ... args)
        : Tuple( sizeof...(args))
        {
            setItems(0, std::forward<Types>(args)...);
        }

        virtual ~TupleN()
        { }

    private:
        template <class Car, class... Cdr>
        void setItems(int idx, Car&& car, Cdr&&... cdr)
        {
            setItem(idx,        std::forward<Car>(car));
            setItems(idx + 1,   std::forward<Cdr>(cdr)...);
        }

        void setItems(int)  // recursion terminator
        {}
    }

http://en.cppreference.com/w/cpp/language/parameter_pack

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License