I have a c++ function taking variable number of arguments.
char const* Fun(int num, ...)
{
....//does some processing on the arguments passed
}
Boost Python code for exposing this function is written as,
using namespace boost::python;
BOOST_PYTHON_MODULE( lib_boost )
{
def( "Fun", Fun );
}
while compiling this code gives the below error
In file included from /boost_1_42_0/boost/python/data_members.hpp:15,
from /boost_1_42_0/boost/python/class.hpp:17,
from /boost_1_42_0/boost/python.hpp:18,
from Lib_boost.h:3,
from Lib_boost.cpp:1: /boost_1_42_0/boost/python/make_function.hpp: In function
'boost::python::api::object boost::python::make_function(F) [with F =
const char* ()(int, ...)]': /boost_1_42_0/boost/python/def.hpp:82:
instantiated from 'boost::python::api::object
boost::python::detail::make_function1(T, ...) [with T = const char
()(int, ...)]' /boost_1_42_0/boost/python/def.hpp:91: instantiated
from 'void boost::python::def(const char, Fn) [with Fn = const char*
()(int, ...)]' Lib_boost.cpp:540: instantiated from here
/boost_1_42_0/boost/python/make_function.hpp:104: error: invalid
conversion from 'const char ()(int, ...)' to 'const char
()(int) /boost_1_42_0/boost/python/make_function.hpp:104: error:
initializing argument 1 of 'boost::mpl::vector2
boost::python::detail::get_signature(RT ()(T0), void*) [with RT =
const char*, T0 = int]'
My understanding from the error info above is boost python could not recognize the function taking variable arguments(invalid conversion from 'const char* ()(int, ...)' to 'const char (*)(int)')
Exposing a function with fixed/known set of arguments is not the same for functions taking variable arguments.
How to expose a function with variable arguments?
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