How to expose a c++ function taking variable arguments in boost python

匆匆过客 提交于 2019-12-04 17:43:00

if you are using c++11 then following could work ( tested on g++-4.8.2 )

#include <boost/python.hpp>
#include <boost/python/list.hpp>
#include <vector>
#include <string>
#include <cstdarg>
#include <cassert>

using namespace boost::python;

template <int... Indices>
struct indices
{
    using next = indices<Indices..., sizeof...(Indices)>;
};

template <int N>
struct build_indices
{
    using type = typename build_indices<N-1>::type::next;
};

template <>
struct build_indices<0>
{
    using type = indices<>;
};
template <int N>
using BuildIndices = typename build_indices<N>::type;

template <int num_args>
class unpack_caller
{
private:
    template <typename FuncType, int... I>
    char * call(FuncType &f, std::vector<char*> &args, indices<I...>)
    {
        return f(args.size(), args[I]...);
    }

public:
    template <typename FuncType>
    char * operator () (FuncType &f, std::vector<char*> &args)
    {
        assert( args.size() <= num_args );
        return call(f, args, BuildIndices<num_args>{});
    }
};

//This is your function that you wish to call from python
char * my_func( int a, ... )
{
    //do something ( this is just a sample )
    static std::string ret;

    va_list ap;
    va_start (ap, a);
    for( int i = 0; i < a; ++i)
    {
        ret += std::string( va_arg (ap, char * ) );
    }

    va_end (ap);
    return (char *)ret.c_str();
}

std::string my_func_overload( list & l )
{
    extract<int> str_count( l[0] );
    if( str_count.check() )
    {
        int count = str_count();
        std::vector< char * > vec;
        for( int index = 1; index <= count; ++index )
        {
            extract< char * > str( l[index] );
            if( str.check() )
            {
                //extract items from list and build vector
                vec.push_back( str() );
            }
        }
        //maximum 20 arguments will be processed.
        unpack_caller<20> caller;

        return std::string( caller( my_func, vec ) );
    }

    return std::string("");
}

BOOST_PYTHON_MODULE(my_module)
{
    def("my_func", my_func_overload )
    ;
}

In python:

Python 2.7.6 (default, Mar 22 2014, 22:59:38) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import my_module as m
>>> m.my_func([5, "my", " first", " five", " string", " arguments"])
'my first five string arguments'
>>>

In this example "char * my_func( int a, ... )" simply concatenates all the string arguments and returns the resulting string.

I find the best way to treat variadic arguments is by using raw_function. This way you get full control in converting your C++ parameters into Python objects:

The wrapper:

using namespace boost::python;

object fun(tuple args, dict kwargs)
{
    char* returned_value;

    for(int i = 0; i < len(args); ++i) {
        // Extract the args[i] into a C++ variable,
        // build up your argument list
    }

    // build your parameter list from args and kwargs 
    // and pass it to your variadic c++ function

    return str(returned_value);
}

The declaration:

def("fun", raw_function(fun, 1) );

raw_function takes two arguments: the function pointer and minimum number of arguments.

You can probably do it by treating the arguments as optional, so long as you know what the maximum count can be. See here: https://wiki.python.org/moin/boost.python/FunctionOverloading

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!