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++库的复杂性而有所不同,但这应该可以帮助你入门。如果有更多的问题可以留言讨论。

相关推荐
用户556918817531 小时前
#从脚本到独立程序:Python + Playwright 批量抓取的完整踩坑记录
python·自动化运维
兵慌码乱15 小时前
基于 MediaPipe 与 PySide2 的手势交互音乐控制系统实现:轻量化视觉交互全流程解析
python·opencv·计算机视觉·人机交互·手势识别·mediapipe·pyside2
luckdewei18 小时前
FastAPI 资产管理系统实战:复杂 ORM 关联、Alembic 迁移与 N+1 查询优化
python
aqi001 天前
15天学会AI应用开发(八)使用向量数据库实现RAG功能
人工智能·python·大模型·ai编程·ai应用
Csvn1 天前
`functools.lru_cache` —— 一行代码搞定缓存加速
后端·python
金銀銅鐵2 天前
[Python] 从《千字文》中随机挑选汉字
后端·python
cup112 天前
[技术复盘] Windows Python 打包实战:Nuitka 环境踩坑总结与 CI 自动化构建全指南
python·ai·环境变量·ci·nuitka·skill
aqi002 天前
15天学会AI应用开发(七)有了大模型为什么还要引入RAG
人工智能·python·大模型·ai编程·ai应用