【问题标题】:How to expose a c++ function taking variable arguments in boost python如何在boost python中公开一个采用可变参数的c++函数
【发布时间】:2014-09-17 02:18:42
【问题描述】:

我有一个带有可变数量参数的 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) [with F = const char* ()(int, ...)]': /boost_1_42_0/boost/python/def.hpp:82:
从 'boost::python::api::object 实例化 boost::python::detail::make_function1(T, ...) [with T = const char
()(int, ...)]' /boost_1_42_0/boost/python/def.hpp:91: 实例化 来自 'void boost::python::def(const char, Fn) [with 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 的参数 1 boost::python::detail::get_signature(RT (
)(T0), void*) [with RT = const char*, T0 = int]'

从上面的错误信息中我的理解是 boost python 无法识别带变量参数的函数(从 'const char* ()(int, ...)' 到 'const char 的无效转换) (*)(int)')

公开具有固定/已知参数集的函数与采用可变参数的函数不同。 如何公开具有可变参数的函数?

【问题讨论】:

  • 可变参数有哪些类型?它们是否保证是一种单一类型,或者它们可以是异构的?为了使用它们,您如何知道它们是什么类型?
  • 第一个参数是整数,其余是异构的(参数的数量也可以变化)
  • 那么你怎么知道如何使用它们呢?请注意, printf() 接受可变参数,但也接受格式字符串,它需要知道如何处理参数。没有这样的东西,你可能会过得很糟糕。
  • 其实我们是在现有的应用程序n中添加boost接口,函数如下 ` char const* Fun(int num, ...) { ....//参数被va_List捕获 va_list vl; va_start(vl,num); //读取 vl va_end(vl); }` 共享链接中提供的示例,函数参数是固定的,类型也是固定的。
  • 建议我为第一个参数是整数后跟'n'个字符串(char *)的情况提供解决方案,其中n从1到20不等

标签: python c++ boost boost-python variadic-functions


【解决方案1】:

如果您使用的是 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, ... )”只是连接所有字符串参数并返回结果字符串。

【讨论】:

    【解决方案2】:

    我发现处理可变参数的最佳方法是使用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 有两个参数:函数指针和最小参数个数。

    【讨论】:

    • // 从 args 和 kwargs 构建您的参数列表 // 并将其传递给您的可变参数 c++ 函数您将如何做到这一点?
    • @Icarus3:如果您的可变参数函数没有采用 va_list 的版本(例如 vprintf 而不是 printf),您将无法在 C++ 中为传递给可变参数函数的每个单独的参数组合实现一个包装函数。或者您将不得不自己编写程序集来调用可变参数函数。如果您想了解令人讨厌的细节,请尝试阅读以下内容:gallium.inria.fr/~rainey/articles/ml-varargs.pdf 或尝试使用 libffi,这可能会帮助您做同样的事情,但无需自己编写程序集:eli.thegreenplace.net/2013/03/04/…
    • 感谢您的评论。似乎是一本不错的读物(pdf)。我正在考虑使用可变参数模板函数作为包装器,然后使用 std::forward 调用 C 可变参数函数。挑战在于使用 boost::bind 和索引(绑定参数)来调用我的可变参数模板。
    【解决方案3】:

    您可以通过将参数视为可选来做到这一点,只要您知道最大计数可以是多少。见这里:https://wiki.python.org/moin/boost.python/FunctionOverloading

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-04
      • 2016-02-25
      • 2015-02-21
      • 1970-01-01
      • 1970-01-01
      • 2012-03-03
      相关资源
      最近更新 更多