如何在Boost.Python中暴露一个接受可变参数的C++函数
我有一个C++的函数,它可以接收不同数量的参数。
char const* Fun(int num, ...)
{
....//does some processing on the arguments passed
}
为了让这个函数在Boost Python中可用,我写了以下代码:
using namespace boost::python;
BOOST_PYTHON_MODULE( lib_boost )
{
def( "Fun", Fun );
}
但是在编译这段代码时,出现了以下错误:
错误信息显示在以下文件中:/boost_1_42_0/boost/python/data_members.hpp:15,
从/boost_1_42_0/boost/python/class.hpp:17引入,
再从/boost_1_42_0/boost/python.hpp:18引入,
再从Lib_boost.h:3引入,
最后从Lib_boost.cpp:1引入。
错误出现在/boost_1_42_0/boost/python/make_function.hpp中,具体是在函数
'boost::python::api::object boost::python::make_function(F) [F = const char* ()(int, ...)]':
在/boost_1_42_0/boost/python/def.hpp:82行,
这个函数的实例化来自于'boost::python::api::object
boost::python::detail::make_function1(T, ...) [T = const char* ()(int, ...)]',
在/boost_1_42_0/boost/python/def.hpp:91行,
这个函数的实例化来自于'void boost::python::def(const char*, Fn) [Fn = const char*
()(int, ...)]',在Lib_boost.cpp:540行,
错误信息显示在/boost_1_42_0/boost/python/make_function.hpp:104行:
错误:无法将'const char* ()(int, ...)'转换为'const char*
()(int)'。
在/boost_1_42_0/boost/python/make_function.hpp:104行:
错误:初始化'boost::mpl::vector2
boost::python::detail::get_signature(RT ()(T0), void*) [RT = const char*, T0 = int]'的第一个参数时出错。
根据上面的错误信息,我理解是Boost Python无法识别这个接收可变参数的函数(无法将'const char* ()(int, ...)'转换为'const char*(int)')。
对于接收固定参数的函数,暴露它们的方式和接收可变参数的函数是不同的。那么,如何才能暴露一个接收可变参数的函数呢?
3 个回答
你可以把这些参数当作可选的,只要你知道最多能有多少个参数就行。具体可以参考这里:https://wiki.python.org/moin/boost.python/FunctionOverloading
我发现处理可变参数的最佳方法是使用 raw_function
。这样你可以完全控制如何将你的 C++ 参数转换成 Python 对象:
这是一个包装器:
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);
}
这是声明:
def("fun", raw_function(fun, 1) );
raw_function
接受两个参数:一个是函数指针,另一个是最少参数数量。
如果你在使用C++11,那么下面的代码可能会有效(在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 )
;
}
在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'
>>>
在这个例子中,“char * my_func( int a, ... )”这个函数的作用是把所有的字符串参数连接在一起,然后返回合成的字符串。