C++ List 到 Python List 的转换

当我们编写 C++ 库的封装器通常涉及使用一种跨语言的接口技术,比如使用C接口或者使用特定的跨语言库,比如SWIG(Simplified Wrapper and Interface Generator)或者Pybind11。这里我将简要介绍如何使用Pybind11来封装一个C++库,以便在Python中使用。

1、问题背景

在编写 C++ 库的封装器时,需要将 C++ 中的 list 容器转换为 Python 中的 list。由于 C++ 库不能被修改,因此希望避免使用 vector 来替代 list。

为了更好地理解这种情况,使用 list 作为代理来注册从 C++ 到 Python 的转换(只读)。当前的实现可以编译,Python 可以正常导入,并且可以创建对象,但是在调用数据成员时会出错。

Python 控制台输出:

python 复制代码
In [1]: import my_list

In [2]: x = my_list.Bob()

In [3]: x.foos
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-3-2f015d13a87d> in <module>()
----> 1 x.foos

TypeError: No Python class registered for C++ class std::list<int, std::allocator<int> >

C++ 文件:

c++ 复制代码
#include <list>
#include <boost/python.hpp>

#include <boost/foreach.hpp>
#ifndef FOREACH
    #define FOREACH BOOST_FOREACH
#endif

using namespace std;
using namespace boost::python;

template<typename T>
struct list_to_list
{
    static PyObject* convert(const std::list<T>& src)
    {
        boost::python::list result;
        FOREACH (const T& val, src)
        {
            result.append(val);
        }

        return incref(result.ptr());
    }
};

struct Bob
{
    std::list<int> foos;
};

BOOST_PYTHON_MODULE(my_list)
{
    using namespace boost::python;

    to_python_converter<std::list<int>, list_to_list<int> >();

    class_<Bob>("Bob")
        .def_readonly("foos", &Bob::foos)
    ;
}

2、解决方案

如常见问题解答中所述,通过 def_readonly()def_readwrite()add_property() 暴露的属性,使用默认策略不会使用自定义转换器。要解决此问题,请用 add_property() 替换 def_readonly()def_readwrite(),提供一个 boost::python::return_value_policy,其类型为 boost::python::return_by_value

在原始代码中,它应该是:

c++ 复制代码
BOOST_PYTHON_MODULE(my_list)
{
  using namespace boost::python;

  to_python_converter<std::list<int>, list_to_list<int> >();

  class_<Bob>("Bob")
    .add_property("foos", make_getter(&Bob_foos,
        return_value_policy<return_by_value>())) 
    ;
}

完整的示例如下:

c++ 复制代码
#include <list>
#include <boost/assign/list_of.hpp>
#include <boost/foreach.hpp>
#include <boost/python.hpp>

namespace python = boost::python;

/// @brief Type to convert from an iterable to a Python list.
template <typename T>
struct list_to_list
{
  static PyObject* convert(const std::list<T>& container)
  {
    python::list result;
    BOOST_FOREACH(const T& value, container)
      result.append(value);
    return python::incref(result.ptr());
  }
};

/// @brief mockup type.
struct Spam
{
  Spam()
  {
    foos = boost::assign::list_of(1)(2)(3)(5);
  }

  std::list<int> foos;
};

BOOST_PYTHON_MODULE(example)
{
  // Enable std::list<int> to Python list conversion.
  python::to_python_converter<std::list<int>, list_to_list<int> >();

  python::class_<Spam>("Spam")
    .add_property("foo", python::make_getter(&Spam::foos,
          python::return_value_policy<python::return_by_value>()))
    ;
}

相应的用法:

python 复制代码
>>> import example
>>> spam = example.Spam()
>>> spam.foo
[1, 2, 3, 5]

上面这个就是一个简单的例子,展示了如何使用Pybind11来封装一个C++库。具体的细节可能会因为我们的C++库的复杂性而有所不同,但这应该可以帮助你入门。如果有更多的问题可以留言讨论。

相关推荐
这里有鱼汤几秒前
“对象”?对象你个头!——Python世界观彻底崩塌的一天
后端·python
尘浮72810 分钟前
60天python训练计划----day59
开发语言·python
wh393313 分钟前
使用Python将PDF转换成word、PPT
python·pdf·word
船长@Quant30 分钟前
数学视频动画引擎Python库 -- Manim Voiceover 语音服务 Speech Services
python·数学·manim·动画引擎·语音旁白
Chef_Chen38 分钟前
从0开始学习R语言--Day39--Spearman 秩相关
开发语言·学习·r语言
不学会Ⅳ1 小时前
Mac M芯片搭建jdk源码环境(jdk24)
java·开发语言·macos
2401_881244401 小时前
牛客周赛99
c++
好开心啊没烦恼2 小时前
Python 数据分析:计算,分组统计1,df.groupby()。听故事学知识点怎么这么容易?
开发语言·python·数据挖掘·数据分析·pandas
lljss20203 小时前
Python11中创建虚拟环境、安装 TensorFlow
开发语言·python·tensorflow
空中湖3 小时前
tensorflow武林志第二卷第九章:玄功九转
人工智能·python·tensorflow