C/C++ 工程提供 Python 接口,有利于融合进 Python 的生态。现在 Python 在应用层,有其得天独厚的优势。尤其因为人工智能和大数据的推波助澜, Python 现在以及未来,将长期是最流行的语言之一。

那 C/C++ 怎么提供 Python 接口呢?

  1. ctypes: C 与 Python 绑定, Python 内建模块
  2. Boost.Python: C++ 与 Python 绑定, Boost 模块
  3. pybind11: C++11 与 Python 绑定, 减去了旧 C++ 支持,更轻量化

本文将介绍 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.txtadd_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 接口的更多相关文章

  1. 虹软最新版 python 接口 完整版

    虹软最新版 python 接口 完整版 当前开源的人脸检测模型,识别很多,很多小伙伴也踩过不少坑.相信不少使用过dlib和facenet人脸识别的小伙伴都有这样的疑惑,为什么论文里高达99.8以上的准 ...

  2. Python一秒提供Rest接口

    Python一秒提供Rest接口 使用的是Anaconda安装的Python环境; 新建py文件(例如:restapi.py) # -*- coding: utf-8 -*- from flask i ...

  3. 利用Clang(Python接口)来解析C++

    1 背景说明 最近希望利用开源库来解析C++头文件,并做一些自动翻译.自动注释之类的工作.经过两天的调研,发现clang最有希望满足需求.clang提供了三套接口来共外部使用,liblang最适合作为 ...

  4. python接口自动化(三十九)- logger 日志 - 上(超详解)

    简介 Python的logging模块提供了通用的日志系统,可以方便第三方模块或者是应用使用.这个模块提供不同的日志级别,并可以采用不同的方式记录日志,比如文件,HTTP GET/POST,SMTP, ...

  5. 机器学习caffe环境搭建——redhat7.1和caffe的python接口编译

    相信看这篇文章的都知道caffe是干嘛的了,无非就是深度学习.神经网络.计算机视觉.人工智能这些,这个我就不多介绍了,下面说说我的安装过程即遇到的问题,当然还有解决方法. 说下我的环境:1>虚拟 ...

  6. caffe的python接口学习(1):生成配置文件

    caffe是C++语言写的,可能很多人不太熟悉,因此想用更简单的脚本语言来实现.caffe提供matlab接口和python接口,这两种语言就非常简单,而且非常容易进行可视化,使得学习更加快速,理解更 ...

  7. windows10下基于vs2015的 caffe安装教程及python接口实现

    啦啦啦:根据网上的教程前一天安装失败,第二天才安装成功.其实caffe的安装并不难,只是网上的教程不是很全面,自己写一个,留作纪念. 准备工作 Windows10 操作系统 vs2015(c++编译器 ...

  8. 【caffe】Caffe的Python接口-官方教程-00-classification-详细说明(含代码)

    00-classification 主要讲的是如何利用caffenet(与Alex-net稍稍不同的模型)对一张图片进行分类(基于imagenet的1000个类别) 先说说教程到底在哪(反正我是找了半 ...

  9. MySQL Connector/Python 接口 (一)

    这里仅介绍 MySQL 官方开发的 Python 接口,参见这里: https://dev.mysql.com/doc/connector-python/en/ Chapter 1 Introduct ...

随机推荐

  1. SSM三大框架的整合

    好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star,更多文章请前往:目录导航 在Java后端开发领域,Spri ...

  2. Jmeter无法监听服务器4444端口

    阿里云服务器开放了4444端口 jmeter还是无法监听: 解决方法: 阿里云安全组添加端口5555 服务器中启动监听插件使用5555端口,使用命令:java -jar ./CMDRunner.jar ...

  3. Java动态代理(AOP)

    目录 一.代理 1. 什么是代理? 2. 使用代理模式的作用 3. 实现代理的方式 二.静态代理 1. 模拟用户购买u盘 2. 静态代理的缺点 三.动态代理 四. JDK 动态代理 1. Invoca ...

  4. 可能是Asp.net Core On host、 docker、kubernetes(K8s) 配置读取的最佳实践

    写在前面 为了不违反广告法,我竭尽全力,不过"最佳实践"确是标题党无疑,如果硬要说的话 只能是个人最佳实践. 问题引出 ​ 可能很多新手都会遇到同样的问题:我要我的Asp.net ...

  5. Python面向对象,站在更高的角度来思考

    开篇 面向过程编程和面向对象编程是两种基本的编程思想. 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案例的人,却不知道如何去 ...

  6. 关于css布局中,inline-block元素间隙的处理方法

    关于inline-block元素间隙的处理 参考橱窗外的小孩,原文链接https://www.cnblogs.com/showcase/p/10469361.html 如下,两个inline-bloc ...

  7. 从零搭建Spring Boot脚手架(4):手写Mybatis通用Mapper

    1. 前言 今天继续搭建我们的kono Spring Boot脚手架,上一文把国内最流行的ORM框架Mybatis也集成了进去.但是很多时候我们希望有一些开箱即用的通用Mapper来简化我们的开发.我 ...

  8. 01-java实现动态数组

    01-手撸动态数组 本篇是恋上数据结构第一季个人总结 借鉴https://juejin.im/post/6844904001478066183#heading-0 本人git https://gith ...

  9. EfficientNet

  10. hdfs学习(二)

    一.HDFS文件限额配置 在多人共用HDFS的环境下,配置设置非常重要.特别是在Hadoop处理大量资料的环境,如果没有配额管理,很容易把所有的空间用完造成别人无法存取.Hdfs的配额设定是针对目录而 ...