服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - pybind11: C++ 工程提供 Python 接口的实例代码

pybind11: C++ 工程提供 Python 接口的实例代码

2021-09-27 10:31GoCodingInMyWay C/C++

这篇文章主要介绍了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 添加子模块,最好固定为某个版本:

?
1
2
3
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 的模块了。

?
1
2
3
4
5
6
7
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 链接该动态库就可以了。

?
1
target_link_libraries(example_pb PUBLIC example)

绑定一个函数

我们先实现一个 add 函数,

?
1
2
3
int add(int i, int j) {
 return i + j;
}

为了简化工程,可以直接实现在 example_pb.cpp 里,

?
1
2
3
4
5
6
7
8
9
10
11
12
13
#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 编译就完成了。

示例代码

绑定一个类

我们先实现一个定时触发器的类。使用如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#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

该类的声明如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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 绑定实现如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#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

?
1
2
3
4
5
6
7
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 里调用了:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/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

示例代码

运行示例代码

获取代码,

?
1
2
3
4
5
git clone https://github.com/ikuokuo/start-pybind11.git
 
# 获取子模块
cd start-pybind11/
git submodule update --init

编译安装,

?
1
2
3
4
# 依赖 cmake
 
cd start-pybind11/
make install

编译结果,

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ 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

添加路径,

?
1
2
3
4
$ 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

运行示例,

?
1
2
3
4
5
6
$ 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 接口的文章就介绍到这了,更多相关pybind11: C++ 工程如何提供 Python 接口内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://www.cnblogs.com/gocodinginmyway/p/13611559.html

延伸 · 阅读

精彩推荐
  • C/C++c/c++内存分配大小实例讲解

    c/c++内存分配大小实例讲解

    在本篇文章里小编给大家整理了一篇关于c/c++内存分配大小实例讲解内容,有需要的朋友们可以跟着学习参考下。...

    jihite5172022-02-22
  • C/C++OpenCV实现拼接图像的简单方法

    OpenCV实现拼接图像的简单方法

    这篇文章主要为大家详细介绍了OpenCV实现拼接图像的简单方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    iteye_183805102021-07-29
  • C/C++关于C语言中E-R图的详解

    关于C语言中E-R图的详解

    今天小编就为大家分享一篇关于关于C语言中E-R图的详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看...

    Struggler095962021-07-12
  • C/C++使用C++制作简单的web服务器(续)

    使用C++制作简单的web服务器(续)

    本文承接上文《使用C++制作简单的web服务器》,把web服务器做的功能稍微强大些,主要增加的功能是从文件中读取网页并返回给客户端,而不是把网页代码...

    C++教程网5492021-02-22
  • C/C++深入C++拷贝构造函数的总结详解

    深入C++拷贝构造函数的总结详解

    本篇文章是对C++中拷贝构造函数进行了总结与介绍。需要的朋友参考下...

    C++教程网5182020-11-30
  • C/C++C语言main函数的三种形式实例详解

    C语言main函数的三种形式实例详解

    这篇文章主要介绍了 C语言main函数的三种形式实例详解的相关资料,需要的朋友可以参考下...

    ieearth6912021-05-16
  • C/C++C语言实现双人五子棋游戏

    C语言实现双人五子棋游戏

    这篇文章主要为大家详细介绍了C语言实现双人五子棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    两片空白7312021-11-12
  • C/C++c/c++实现获取域名的IP地址

    c/c++实现获取域名的IP地址

    本文给大家汇总介绍了使用c/c++实现获取域名的IP地址的几种方法以及这些方法的核心函数gethostbyname的详细用法,非常的实用,有需要的小伙伴可以参考下...

    C++教程网10262021-03-16