pybind11: C++ 工程如何提供 Python 接口
C/C++ 工程提供 Python 接口,有利于融合进 Python 的生态。现在 Python 在应用层,有其得天独厚的优势。尤其因为人工智能和大数据的推波助澜, Python 现在以及未来,将长期是最流行的语言之一。
那 C/C++ 怎么提供 Python 接口呢?
- ctypes: C 与 Python 绑定, Python 内建模块
- Boost.Python: C++ 与 Python 绑定, Boost 模块
- pybind11: C++11 与 Python 绑定, 减去了旧 C++ 支持,更轻量化
本文将介绍 pybind11 的环境准备与入门使用。
- pybind11: https://github.com/pybind/pybind11
环境准备
pybind11 是一个 header-only
的库,换句话说,只需要 C++ 项目里直接 include pybind11 的头文件就能使用。
这里则介绍如何于 CMake 里引入 pybind11 。而更多编译系统的介绍,可见官方文档 Build systems 。
获取 pybind11
可以 git submodule
添加子模块,最好固定为某个版本:
git submodule add https://github.com/pybind/pybind11.git third_party/pybind11-2.5.0
cd third_party/pybind11-2.5.0/
git checkout tags/v2.5.0
或者,直接获取源码,放进相应子目录即可。
添加进 CMake
CMakeLists.txt
里 add_subdirectory
pybind11 的路径,再用其提供的 pybind11_add_module
就能创建 pybind11 的模块了。
cmake_minimum_required(VERSION 3.1)
project(start-pybind11 VERSION 0.1.0 LANGUAGES C CXX)
set(MY_PYBIND ${MY_CURR}/third_party/pybind11-2.5.0)
add_subdirectory(${MY_PYBIND})
pybind11_add_module(example_pb example_pb.cpp)
如果想在已有 C++ 动态库上扩展 pybind11 绑定,那么 target_link_libraries
链接该动态库就可以了。
target_link_libraries(example_pb PUBLIC example)
绑定一个函数
我们先实现一个 add
函数,
int add(int i, int j) {
return i + j;
}
为了简化工程,可以直接实现在 example_pb.cpp
里,
#include <pybind11/pybind11.h>
namespace py = pybind11;
int add(int i, int j) {
return i + j;
}
PYBIND11_MODULE(example_pb, m) {
m.doc() = "example_pb bindings";
m.def("add", &add, "A function which adds two numbers");
}
之后,于 CMakeLists.txt
所在目录,执行 cmake
编译就完成了。
示例代码
绑定一个类
我们先实现一个定时触发器的类。使用如下:
#include <iostream>
#include "tick.h"
int main(int argc, char const *argv[]) {
(void)argc;
(void)argv;
Tick tick(500, 5000);
tick.SetTickEvent([&tick](std::int64_t elapsed_ms) {
std::cout << "elapsed: " << elapsed_ms << " ms" << std::endl;
if (elapsed_ms >= 2000) {
tick.Stop();
}
});
tick.Start();
tick.WaitLifeOver();
return 0;
}
运行结果:
$ ./_output/bin/cpp_thread_callback/tick_test
elapsed: 0 ms
elapsed: 500 ms
elapsed: 1000 ms
elapsed: 1500 ms
elapsed: 2000 ms
该类的声明如下:
using TickEvent = std::function<void(std::int64_t elapsed_ms)>;
using TickRunCallback = std::function<void()>;
class Tick {
public:
using clock = std::chrono::high_resolution_clock;
Tick(std::int64_t tick_ms,
std::int64_t life_ms = std::numeric_limits<std::int64_t>::max());
Tick(TickEvent tick_event, std::int64_t tick_ms,
std::int64_t life_ms = std::numeric_limits<std::int64_t>::max(),
TickRunCallback run_beg = nullptr,
TickRunCallback run_end = nullptr);
virtual ~Tick();
bool IsRunning() const;
void Start();
void Stop(bool wait_life_over = false);
const std::chrono::time_point<clock> &GetTimeStart() const;
void SetTickEvent(TickEvent &&tick_event);
void SetTickEvent(const TickEvent &tick_event);
void SetRunBegCallback(TickRunCallback &&run_beg);
void SetRunBegCallback(const TickRunCallback &run_beg);
void SetRunEndCallback(TickRunCallback &&run_end);
void SetRunEndCallback(const TickRunCallback &run_end);
void WaitLifeOver();
protected:
// ...
};
然后, pybind11 绑定实现如下:
#include <pybind11/pybind11.h>
#include <pybind11/chrono.h>
#include <pybind11/functional.h>
#include <memory>
#include "cpp/cpp_thread_callback/tick.h"
namespace py = pybind11;
using namespace pybind11::literals; // NOLINT
PYBIND11_MODULE(tick_pb, m) {
m.doc() = "tick_pb bindings";
py::class_<Tick, std::shared_ptr<Tick>>(m, "Tick")
.def(py::init<std::int64_t, std::int64_t>())
.def(py::init<TickEvent, std::int64_t, std::int64_t,
TickRunCallback, TickRunCallback>())
.def_property_readonly("is_running", &Tick::IsRunning)
.def("start", &Tick::Start)
.def("stop", &Tick::Stop, "wait_life_over"_a = false)
.def("get_time_start", &Tick::GetTimeStart)
.def("set_tick_event", [](Tick &self, const TickEvent &tick_event) {
self.SetTickEvent(tick_event);
})
.def("set_run_beg_callback", [](Tick &self,
const TickRunCallback &run_beg) {
self.SetRunBegCallback(run_beg);
})
.def("set_run_end_callback", [](Tick &self,
const TickRunCallback &run_end) {
self.SetRunEndCallback(run_end);
})
.def("wait_life_over", &Tick::WaitLifeOver,
py::call_guard<py::gil_scoped_release>());
}
编译出动态库后,把路径添加进 PYTHONPATH
:
export PYTHONPATH=<path>:$PYTHONPATH
# 依赖其他动态库的话,把路径添加进 LIBRARY_PATH
# Linux
export LD_LIBRARY_PATH=<path>:$LD_LIBRARY_PATH
# macOS
export DYLD_LIBRARY_PATH=<path>:$DYLD_LIBRARY_PATH
之后,就可以于 Python 里调用了:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# pylint: disable=missing-docstring, import-error
import tick_pb as tick
def _main():
t = tick.Tick(lambda elapsed_ms: print(f"elapsed: {elapsed_ms} ms"),
500, 1000,
lambda: print("run beg"), lambda: print("run end"))
t.start()
t.wait_life_over()
if __name__ == "__main__":
_main()
运行结果:
$ python src/pybind/cpp_thread_callback/tick_test.py
run beg
elapsed: 0 ms
elapsed: 500 ms
elapsed: 1000 ms
run end
示例代码
运行示例代码
获取代码,
git clone https://github.com/ikuokuo/start-pybind11.git
# 获取子模块
cd start-pybind11/
git submodule update --init
编译安装,
# 依赖 cmake
cd start-pybind11/
make install
编译结果,
$ tree _install
_install
├── bin
│ └── cpp_thread_callback
│ └── tick_test
└── lib
├── cpp_thread_callback
│ ├── libtick.0.1.0.dylib
│ ├── libtick.0.1.dylib -> libtick.0.1.0.dylib
│ ├── libtick.dylib -> libtick.0.1.dylib
│ ├── tick_pb.0.1.0.cpython-37m-darwin.so
│ ├── tick_pb.0.1.cpython-37m-darwin.so -> tick_pb.0.1.0.cpython-37m-darwin.so
│ └── tick_pb.cpython-37m-darwin.so -> tick_pb.0.1.cpython-37m-darwin.so
└── first_steps
├── first_steps_pb.0.1.0.cpython-37m-darwin.so
├── first_steps_pb.0.1.cpython-37m-darwin.so -> first_steps_pb.0.1.0.cpython-37m-darwin.so
├── first_steps_pb.cpython-37m-darwin.so -> first_steps_pb.0.1.cpython-37m-darwin.so
├── libfirst_steps.0.1.0.dylib
├── libfirst_steps.0.1.dylib -> libfirst_steps.0.1.0.dylib
└── libfirst_steps.dylib -> libfirst_steps.0.1.dylib
5 directories, 13 files
添加路径,
$ source setup.bash first_steps cpp_thread_callback
DYLD_LIBRARY_PATH, PYTHONPATH
+ /Users/John/Workspace/Self/ikuokuo/start-pybind11/_install/lib/first_steps
+ /Users/John/Workspace/Self/ikuokuo/start-pybind11/_install/lib/cpp_thread_callback
运行示例,
$ python src/pybind/cpp_thread_callback/tick_test.py
run beg
elapsed: 0 ms
elapsed: 500 ms
elapsed: 1000 ms
run end
结语
Go coding!
pybind11: C++ 工程如何提供 Python 接口的更多相关文章
- 虹软最新版 python 接口 完整版
虹软最新版 python 接口 完整版 当前开源的人脸检测模型,识别很多,很多小伙伴也踩过不少坑.相信不少使用过dlib和facenet人脸识别的小伙伴都有这样的疑惑,为什么论文里高达99.8以上的准 ...
- Python一秒提供Rest接口
Python一秒提供Rest接口 使用的是Anaconda安装的Python环境; 新建py文件(例如:restapi.py) # -*- coding: utf-8 -*- from flask i ...
- 利用Clang(Python接口)来解析C++
1 背景说明 最近希望利用开源库来解析C++头文件,并做一些自动翻译.自动注释之类的工作.经过两天的调研,发现clang最有希望满足需求.clang提供了三套接口来共外部使用,liblang最适合作为 ...
- python接口自动化(三十九)- logger 日志 - 上(超详解)
简介 Python的logging模块提供了通用的日志系统,可以方便第三方模块或者是应用使用.这个模块提供不同的日志级别,并可以采用不同的方式记录日志,比如文件,HTTP GET/POST,SMTP, ...
- 机器学习caffe环境搭建——redhat7.1和caffe的python接口编译
相信看这篇文章的都知道caffe是干嘛的了,无非就是深度学习.神经网络.计算机视觉.人工智能这些,这个我就不多介绍了,下面说说我的安装过程即遇到的问题,当然还有解决方法. 说下我的环境:1>虚拟 ...
- caffe的python接口学习(1):生成配置文件
caffe是C++语言写的,可能很多人不太熟悉,因此想用更简单的脚本语言来实现.caffe提供matlab接口和python接口,这两种语言就非常简单,而且非常容易进行可视化,使得学习更加快速,理解更 ...
- windows10下基于vs2015的 caffe安装教程及python接口实现
啦啦啦:根据网上的教程前一天安装失败,第二天才安装成功.其实caffe的安装并不难,只是网上的教程不是很全面,自己写一个,留作纪念. 准备工作 Windows10 操作系统 vs2015(c++编译器 ...
- 【caffe】Caffe的Python接口-官方教程-00-classification-详细说明(含代码)
00-classification 主要讲的是如何利用caffenet(与Alex-net稍稍不同的模型)对一张图片进行分类(基于imagenet的1000个类别) 先说说教程到底在哪(反正我是找了半 ...
- MySQL Connector/Python 接口 (一)
这里仅介绍 MySQL 官方开发的 Python 接口,参见这里: https://dev.mysql.com/doc/connector-python/en/ Chapter 1 Introduct ...
随机推荐
- GLIBC_2.18 not found 之类的问题解决办法
先到 /usr/lib64 下 ll libstdc++* 会发现 libstdc++.so.6 指向了一个新的.比如我这里之前指向6.0.22这个版本,报错 此时只要重新建立软连接 将软连接指向旧的 ...
- Spring bean作用范围
1,singleton Spring ioc 容器中仅有一个 Bean 实例,Bean 以单例的方式存在:2,prototype 每次从容器中调用 Bean 时,都返回一个新的实例:3,request ...
- java进阶(2)--抽象类
一.抽象类集刺虎: 1.概念 类与类之前有共同特征,将这些共同特征提取出来,行为抽象类 2.抽象类的数据类型: 引用数据类型 3.语法 [修饰符列表]abstract Class 类型{类体} ...
- canvas小画板--(1)平滑曲线
功能需求 项目需求:需要实现一个可以自由书写的小画板 简单实现 对于熟悉canvas的同学来说,这个需求很简单,短短几十行代码就能实现: <!doctype html> <html& ...
- 深度学习模型调优方法(Deep Learning学习记录)
深度学习模型的调优,首先需要对各方面进行评估,主要包括定义函数.模型在训练集和测试集拟合效果.交叉验证.激活函数和优化算法的选择等. 那如何对我们自己的模型进行判断呢?——通过模型训练跑代码,我们可以 ...
- Python使用Tornado+Redis维护ADSL拨号服务器代理池
们尝试维护过一个免费的代理池,但是代理池效果用过就知道了,毕竟里面有大量免费代理,虽然这些代理是可用的,但是既然我们能刷到这个免费代理,别人也能呀,所以就导致这个代理同时被很多人使用来抓取网站,所以当 ...
- 文件上传Upload 学习笔记
整理完自己还有点晕,看来还是得找点靶场自己练习练习Orz 1:客户端JavaScript校验 Burp改包即可 2:服务端对Content-Type进行校验 猜测后,修改对应Content-Type字 ...
- Xlua中LuaBehaviour的实现
简介 在基于lua进行热更新的项目中,我们通常会通过luaBehaviour来让lua文件模拟MonoBehaviour,可以让lua文件拥有一些MonoBehaviour的生命周期,如Enabl ...
- JS学习第五天
循环语句: for(变量 in (容器名)数组名){ 执行的语句块: break: 结束整个循环; continue:结束本次循环, 进入下一次循环: } 双层for循环: 外层循环控制行,内层循环控 ...
- Go 语言中,有时 nil 并不是一个 nil
今天,我遇到了一个 Go FAQ.首先,作为一个小小的 Go 语言测验,看看您是否在 Go playground 中运行该程序之前就能推断出它应该打印出的内容(我已经将程序放在侧边栏中,以防它在 Go ...