展开菜单
首页 精品内容 本月促销 装机必备 Windows macOS软件 IOS软件 Android AI PDF教程 专题
全部分类

当前位置:

首页 > 编程开发 > boost.python笔记

boost.python笔记

Boost.python是Boost库的一部分,用于实现C++与Python无缝互操作,无需修改原有C++代码,但需手写封装代码。将C++模块封装为.so库,使Python直接调用,解决RPC调用性能瓶颈。通过定义导出模块、类、函数及成员变量,支持容器类型导出,如vector。

boost.python笔记

标签: boost.python,python, C++

boost.python笔记


简介

Boost.python是什么?

它是boost库的一部分,随boost一起安装,专门用来打通C++和Python之间的壁垒,让这两门语言能无缝协作。

用Boost.python有什么特别之处?

最大的优点是不需要动你原有的C++代码,而且对C++的各种特性支持得相当好。它不会像SWIG那样生成额外的Python代码,但代价是需要你亲手写一部分C++封装代码。

这里只用到它能力的一小部分——把C/C++实现的功能封装成Python可以直接调用的.so库。具体场景是这样的:有一个C++模块通过thrift封装成了RPC服务,Python代码通过RPC调用来请求服务。问题在于调用频率太高,RPC的开销成了性能瓶颈,所以干脆让Python直接调用原模块的功能。

之前了解过SWIG可以实现这个需求,文档都开始看了。转念一想,有没有别的办法?在stack overflow上搜了一圈,发现不少人推荐Boost.python,于是决定拿它来试试水。

如何使用Boost.python

首先,前提是开发环境得配齐:

安装了boost开发环境,头文件和动态库都得有。
安装了python开发环境,头文件和动态库也得有。

然后就是写代码了,这一步怎么都绕不过去!

需要动手的地方有两个。一个是xxxxxx_wrapper.cpp文件——文件名随便起,核心目的是定义要导出的Python模块名称,以及要暴露出去的类、函数等等。另一个是修改你的Makefile,用来编译、链接这个.so库。

先看看xxxxxx_wrapper.cpp长什么样。一般情况下,它的内容跟下面的代码差不多。

#include // 其他需要包含的头文件,与具体业务有关 namespace py = boost::python; // 其他函数,可能包括一些用于类型转换和封装的 BOOST_PYTHON_MODULE(my_module_name) { // 导出普通函数 def("fun_name_in_python", &fun_name_in_c); // 导出类及部分成员 class_("ClassNameInPython", init()) //类名,默认构造函数 .def(init()) //其他构造函数 .def("memberFunNameInPython", &ClassNameInCpp::memberFunNameInCpp) //成员函数 .def_readwrite("dataMemberInPython", &ClassNameInCpp::dataMemberInCpp) //普通成员变量 .def_readonly("dataMemberInPython_2", &ClassNameInCpp::dataMemberInCpp_2)//只读成员变量 ; }

这个文件的核心目的全在BOOST_PYTHON_MODULE里——定义要导出给Python的所有东西。

在Makefile里,需要加一条规则来编译导出的.so文件。通用的编译命令长这样,这里把编译和链接写在一起了。

g++ -o my_module_name.so -shared -fPIC -I${BOOST_INCLUDE_PATH} -I${PYTHON_INCLUDE_PATH} -L${BOOST_LIB_DIR} -lboost_python ${MY_SRC_FILES}

各个参数的作用如下,其他参数就得看具体项目的业务逻辑了:

-o my_module_name.so——这里的模块名必须和xxxxxx_wrapper.cpp里BOOST_PYTHON_MODULE(my_module_name)保持一致;
-I${BOOST_INCLUDE_PATH} -I${PYTHON_INCLUDE_PATH}是编译阶段需要的;
-L${BOOST_LIB_DIR} -lboost_python -shared -fPIC是链接阶段需要的;
${MY_SRC_FILES}包含了xxxxxxx_wrapper.cpp以及业务逻辑需要的其他.cpp、.c文件。

至此,my_module_name.so这个可以被Python调用的模块就诞生了。来测试一下效果:

>>>import my_moduel_name >>>help(my_module_name)

可以看到被导出的类及函数,然后就能像操作普通Python对象那样使用它们了。

如何写wrapper

用一个实例来讲解,内容涵盖普通函数、类、数据成员、成员函数、通过参数传递结果、容器。其他更复杂的特性这里没用到,也没有测试过。

先看看业务逻辑的代码。包含一个类、一个以类对象为参数的函数、一个通过引用修改类对象的函数。

//test_class.h // 定义一个类 class A { public: A(){privateVal=0;}//默认构造函数 A(int val){privateVal=val;} //带参数的构造函数 void set(int val){privateVal=val;}//成员函数 int get() const {return privateVal;}; //成员函数 int publicVal;//公共数据成员 private: int privateVal; //私有数据成员 }; int addA(A &a, int addVal); //普通函数,有返回值,通过引用修改参数 void printA(const A& a);

//test_class.cpp #include #include "test_class.h" int addA(A &a, int addVal){ int val = a.get(); val += addVal; a.set(val); return val; } void printA(const A& a){ printf("%d\n", a.get()); }

然后是wrapper.cpp文件,这里实际叫test_class_wrapper.cpp。

//test_class_wrapper.cpp #include #include "test_class.h" BOOST_PYTHON_MODULE(test_class){ using namespace boost::python; // 导出类 class_("A", init<>())//如果默认构造函数没有参数,可以省略 .def(init()) //其他构造函数 .def("get", &A::get)//成员函数 .def("set", &A::set)//成员函数 .def_readwrite("publicVal", &A::publicVal)//数据成员,当然是公共的 ; def("printA", &printA); def("addA", &addA); }

用Python命令行测试一下:

>>>import test_class >>>a = test_class.A(5) >>>ret = addA(a, 10) >>>print ret 15 >>>print a.get() 15

到目前为止,整个过程相当顺畅。需要额外写的代码很少,也很规整,接近某种IDL的写法——只需要“声明”一下,剩下的事情都交给编译器和库去处理。但有些时候就没这么顺利了,可能需要额外写一些转换和封装代码。比如前面提到过的容器,上面的例子就没有涉及。

接下来,对上面的例子做了一些扩展。第一,给类A增加了一个vector成员,需要在Python代码里引用这个成员;第二,增加了一个以vector为参数的函数,需要在Python代码里直接调用。下面就来解释容器相关的导出。

#include class B; class A{ public: A(){privateVal=0;}//默认构造函数 A(int val){privateVal=val;} //带参数的构造函数 void set(int val){privateVal=val;}//成员函数 int get() const {return privateVal;}; //成员函数 int publicVal;//公共数据成员 std::vector m_vB; private: int privateVal; //私有数据成员 }; class B{ public: B(){} ~B(){} int pos; int len; }; int accumulate(const std::vector& v_A); int addA(A &a, int addVal); //普通函数,有返回值,通过引用修改参数 void printA(const A& a);

#include #include "test_class.h" int addA(A &a, int addVal){ int val = a.get(); val += addVal; a.set(val); return val; } void printA(const A& a){ printf("%d\n", a.get()); } int accumulate(const std::vector& v_A){ int ret = 0; for (size_t i = 0; i < v_A.size(); i++){ ret += v_A[i].get(); } return ret; }

首先,得明白一点:C++里的vector不等于Python里的list,虽然它们看起来挺像的。Boost.python里有与Python的list对应的东西,叫boost::python::list。如果在Python代码里以list为参数调用某个方法,C++代码里这个参数会被自动映射成boost::python::list,而不是vector。那问题来了:如果不想修改原有的C++代码,又想调用以vector为参数的函数,怎么办?

目前了解的方法有两种:

在C++代码里对以vector为参数的函数做一层封装,封装成以boost::python::list为参数的函数,然后导出封装后的函数。在函数里通过boost::python::extract_对list里的每个成员进行提取,把它们从boost::python::object对象变成T类型的对象,存入vector中,再调用以vector为参数的函数。如果需要返回list,或者原函数对vector参数的内容做了修改,还需要把调用函数后的vector里的每个元素再放回list里。

直接导出vector类型。这样一来,vector本身作为一个类型被导出给Python代码,跟普通的类平起平坐。但和普通类不同的是,它通过模板vector_indexing_suite >()导出,自动实现了append、slice、__len__等方法,在Python里可以像操作list那样操作这个导出的vector类。而且,以vector为参数的函数,在通过def导出给Python时,参数会被自动映射成vector_indexing_suite >。同理,Python代码传入的通过vector_indexing_suite导出的容器对象,也会在C++代码里被自动转换成vector,不需要显式写转换函数。

下面的wrapper代码使用了第二种方法——直接导出vector类型。

为什么选它?因为A里有个vector成员,要导出这个成员,就必须导出vector这个类型。否则只能对类A再做一层封装,让它包含一个boost::python::list成员,那就太麻烦了。

#include #include #include "test_class.h" bool operator==(const B& left, const B& right); bool operator==(const A& left, const A& right){ if (left.get() != right.get() || left.publicVal != right.publicVal) return false; if (left.m_vB.size() != right.m_vB.size()) return false; for (size_t i = 0; i < left.m_vB.size(); i ++) { if (!(left.m_vB[i] == right.m_vB[i])) return false; } return true; } bool operator==(const B& left, const B& right){ return (left.pos == right.pos && left.len == right.len); } BOOST_PYTHON_MODULE(test_class){ using namespace boost::python; class_("A", init<>())//如果默认构造函数没有参数,可以省略 .def(init()) //其他构造函数 .def("get", &A::get)//成员函数 .def("set", &A::set)//成员函数 .def_readwrite("publicVal", &A::publicVal)//数据成员,当然是公共的 .def_readwrite("vB", &A::m_vB); class_ >("VecA") .def(vector_indexing_suite >()); class_("B") .def_readwrite("pos", &B::pos) .def_readwrite("len", &B::len); class_ >("VecB") .def(vector_indexing_suite >()); def("printA", &printA); def("addA", &addA); def("accumulate", &accumulate); }

BOOST_PYTHON_MODULE内的代码开始看:

class_的定义和之前的例子相比没什么差别,只是多导出了一个成员.def_readwrite("vB", &A::m_vB)。即使这个成员变量是vector类型的,也不需要特殊对待;
class_就是导出一个类,包含两个公共数据成员,没什么特别的;
class_ >("VecA")class_ >("VecB")是本例的重点——导出了两个不同的vector类型。因为在C++里,vector是一个类模板,vector
vector才是两个具体的类型;
printAaddAaccumulate是三个导出的函数。即使参数是vector
类型,也无需特别对待。

另外,注意为类型A和B定义了==操作符。这是Boost.python在导出某种类型的vector时需要的——内部某个地方会用到==操作符。如果只导出类型本身,不导出类型的向量,是不需要==操作符的,就像前面的例子那样。编译链接完成后,再来看看Python命令行的测试结果:

>>>import test_class >>>a1 = test_class.A() >>>b1 = test_class.B() # 实例化一个B >>>b1.pos = 1 >>>b1.len = 1 >>>b2 = test_class.B()# 实例化另一个B >>>b2.pos = 2 >>>b2.len = 2 >>>a1.vB.append(b1)# a1.vB是vector在python中对应类型的对象,接口类似list,但只能添加B类型的对象 >>>a1.vB.append(b2) >>>print a1.vB[-1].len# a1.vB支持list的下标引用 2 >>>a1.set(1) >>>a2 = test_class.A(2) >>>a3 = test_class.A(3) >>>vA = test_class.VecA()# vector在python中对应的类型 >>>vA.append(a1) >>>vA.append(a2) >>>vA.append(a3) >>>print accumulate(vA)# 调用以vector为参数的函数 6

对于导出的Python模块来说,一切会在Python代码里被引用的变量,其所属类型(基本数据类型除外)都需要被明确导出——也就是说,都得在BOOST_PYTHON_MODULE里定义。比如本例中的vector,虽然没有函数以该类型为参数,但如果想在Python代码里引用A的成员vB,就必须导出它,否则会抛异常。反过来,如果不需要在Python代码里引用这个成员,就不需要导出vector这个类型,而且class_定义中的.def_readwrite("vB", &A::m_vB)也应该去掉。如果不导出vector类型,却在class_定义中通过.def_readwrite("vB", &A::m_vB)导出了该成员,编译不会出问题,使用Python模块也不会出问题——但只要代码引用到A.vB就会抛异常,这相当于埋了一个坑。

从实用的角度看,这样的流程可能会比较有效:首先确定需要导出的函数及类型;然后检查函数(包括成员函数)参数及返回值的类型,非基本类型需要被导出;再检查导出的类成员变量,如果不是基本类型,其类型也要导出。如此反复,直到没有新的类型需要被添加为止。

总结

Boost.python的官方文档感觉不算多,很多问题和trick都是在stack overflow上看到再自己试验的。据了解,Boost.python支持的C++特性比这丰富得多,这里只用到了一小部分而已。

本文内容来源于互联网,如有侵权请联系删除。
作者最新文章
编程开发 Python
相关文章 更多
精品专题 更多
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

macOS软件

正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

IOS软件

正软商城iOS软件专区,精选适用于iPhone和iPad的办公、学习、影音、设计、效率及AI应用,提供功能介绍、适用设备、系统要求和正版获取方式等信息。

AI

正软商城AI软件专区,汇集AI写作、AI绘画、AI视频、AI办公、AI编程、AI翻译、智能客服和数据分析等人工智能工具,提供功能介绍、适用平台、收费方式及正版购买信息。

PDF教程

PDF教程适合刚接触PDF文件的用户,本文整理PDF打开、编辑、转换、合并、压缩等常见基础操作。通过这些教程可以快速了解PDF文件怎么处理,解决办公、学习和资料科整理中的常见问题。使用极轻PDF可在线完成多种PDF操作,适合新手快速上手。

Mac软件 更多
灵活计算器
灵活计算器

灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

赤友清理大师
赤友清理大师

赤友清理大师是一款为 Mac 设计的智能清理优化工具,可精准扫描垃圾、大文件、重复文件等,释放磁盘空间。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

极度公式
极度公式

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

图几
图几

图几是一款适用于 macOS 的截图、标注与美化工具,支持离线操作保障隐私。界面整理和高频系统操作被放到一起考虑,桌面或窗口内容一多时,管理起来会更省心。

密码键盘
密码键盘

密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。

思源笔记
思源笔记

思源笔记是一款本地笔记软件,提供所见即所得的编辑方式,为长文写作带来顺滑的体验。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

Office 365 简体中文
Office 365 简体中文

一款文字处理软件,一种订阅式的跨平台办公软件,基于云平台提供多种服务,通过将 Excel 和 Outlook 等应用与 OneDrive 和 Microsoft Teams 等强大的云服务相结合,Office 365 可让任何人使用任何设备随时随地创建和共享内容。

WALTR PRO
WALTR PRO

WALTR是一款电脑至iOS文件传输转换工具,操作简单,快速实现文件识别与传送。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

CodeExpander
CodeExpander

CodeExpander 是一款快捷短语输入增强工具,通过键入缩写自动展开为自定义文段,提升工作效率。任务管理和过程控制会更完整,持续下载、批量同步或需要稳定传输流程的场景会更适合它。

Mountain Duck
Mountain Duck

Mountain Duck 是一款能将多个网盘挂载到本地的工具,像本地磁盘一样使用网盘。清理链路的完整性会更好一些,做应用卸载、残留处理和空间整理时,通常能少走很多手动排查步骤。

Menuist
Menuist

Menuist 是一款面向 macOS 的 Finder 右键菜单增强工具,主要用来补充新建文件、快捷导航等常用操作,让日常文件管理和访问路径时更高效、更顺手。

Mole
Mole

Mole 是一款专为 Mac 设计的深度清理优化工具,涵盖缓存清理、应用管理及实时状态监控等功能。清理链路的完整性会更好一些,做应用卸载、残留处理和空间整理时,通常能少走很多手动排查步骤。

WINDOWS 更多
Windows 10
Windows 10

Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

极度公式
极度公式

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

密码键盘
密码键盘

密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。

思源笔记
思源笔记

思源笔记是一款本地笔记软件,提供所见即所得的编辑方式,为长文写作带来顺滑的体验。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

傲梅轻松备份
傲梅轻松备份

傲梅轻松备份是一款专业易用的数据备份软件,为重要数据提供安全保障。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。

Office 365 简体中文
Office 365 简体中文

一款文字处理软件,一种订阅式的跨平台办公软件,基于云平台提供多种服务,通过将 Excel 和 Outlook 等应用与 OneDrive 和 Microsoft Teams 等强大的云服务相结合,Office 365 可让任何人使用任何设备随时随地创建和共享内容。

Wise Folder Hider Pro
Wise Folder Hider Pro

Wise Folder Hider Pro 是一款专业级文件和文件夹隐藏加密软件,为私密数据添加多重保护。高频操作更强调就近处理,浏览、整理和跨目录移动文件时,来回切换和重复点击都会少很多。

WALTR PRO
WALTR PRO

WALTR是一款电脑至iOS文件传输转换工具,操作简单,快速实现文件识别与传送。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

CodeExpander
CodeExpander

CodeExpander 是一款快捷短语输入增强工具,通过键入缩写自动展开为自定义文段,提升工作效率。任务管理和过程控制会更完整,持续下载、批量同步或需要稳定传输流程的场景会更适合它。

PinStack
PinStack

PinStack是一款轻量级的Windows平台剪贴板管理工具,优化您的剪贴板使用体验。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

Mountain Duck
Mountain Duck

Mountain Duck 是一款能将多个网盘挂载到本地的工具,像本地磁盘一样使用网盘。清理链路的完整性会更好一些,做应用卸载、残留处理和空间整理时,通常能少走很多手动排查步骤。

Seer
Seer

Seer是一款在Win平台下的空格键功能增强效率工具,只需轻敲空格键,就能预览几乎任何格式的文件。它更适合把零散的小功能集中起来使用,处理高频琐碎任务时会更省事。

网站备案号:苏ICP备2026018738号-1 联系邮箱:bd@zhengruan.com

Copyright ©2018-2026