pybind11: C++ 工程提供 Python 接口的實(shí)例代碼
C/C++ 工程提供 Python 接口,有利于融合進(jìn) Python 的生態(tài)。現(xiàn)在 Python 在應(yīng)用層,有其得天獨(dú)厚的優(yōu)勢。尤其因?yàn)槿斯ぶ悄芎痛髷?shù)據(jù)的推波助瀾, Python 現(xiàn)在以及未來,將長期是最流行的語言之一。
那 C/C++ 怎么提供 Python 接口呢?
- ctypes: C 與 Python 綁定, Python 內(nèi)建模塊
- Boost.Python: C++ 與 Python 綁定, Boost 模塊
- pybind11: C++11 與 Python 綁定, 減去了舊 C++ 支持,更輕量化
本文將介紹 pybind11 的環(huán)境準(zhǔn)備與入門使用。
pybind11: https://github.com/pybind/pybind11
環(huán)境準(zhǔn)備
pybind11 是一個(gè) header-only 的庫,換句話說,只需要 C++ 項(xiàng)目里直接 include pybind11 的頭文件就能使用。
這里則介紹如何于 CMake 里引入 pybind11 。而更多編譯系統(tǒng)的介紹,可見官方文檔 Build systems 。
獲取 pybind11
可以 git submodule 添加子模塊,最好固定為某個(gè)版本:
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
或者,直接獲取源碼,放進(jìn)相應(yīng)子目錄即可。
添加進(jìn) CMake
CMakeLists.txt 里 add_subdirectory pybind11 的路徑,再用其提供的 pybind11_add_module 就能創(chuàng)建 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++ 動(dòng)態(tài)庫上擴(kuò)展 pybind11 綁定,那么 target_link_libraries 鏈接該動(dòng)態(tài)庫就可以了。
target_link_libraries(example_pb PUBLIC example)
綁定一個(gè)函數(shù)
我們先實(shí)現(xiàn)一個(gè) add 函數(shù),
int add(int i, int j) {
return i + j;
}
為了簡化工程,可以直接實(shí)現(xiàn)在 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 所在目錄,執(zhí)行 cmake 編譯就完成了。
示例代碼
綁定一個(gè)類
我們先實(shí)現(xiàn)一個(gè)定時(shí)觸發(fā)器的類。使用如下:
#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;
}
運(yùn)行結(jié)果:
$ ./_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 綁定實(shí)現(xiàn)如下:
#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>());
}
編譯出動(dòng)態(tài)庫后,把路徑添加進(jìn) PYTHONPATH:
export PYTHONPATH=<path>:$PYTHONPATH # 依賴其他動(dòng)態(tài)庫的話,把路徑添加進(jìn) LIBRARY_PATH # Linux export LD_LIBRARY_PATH=<path>:$LD_LIBRARY_PATH # macOS export DYLD_LIBRARY_PATH=<path>:$DYLD_LIBRARY_PATH
之后,就可以于 Python 里調(diào)用了:
#!/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()
運(yùn)行結(jié)果:
$ python src/pybind/cpp_thread_callback/tick_test.py
run beg
elapsed: 0 ms
elapsed: 500 ms
elapsed: 1000 ms
run end
示例代碼
運(yùn)行示例代碼
獲取代碼,
git clone https://github.com/ikuokuo/start-pybind11.git # 獲取子模塊 cd start-pybind11/ git submodule update --init
編譯安裝,
# 依賴 cmake cd start-pybind11/ make install
編譯結(jié)果,
$ 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
運(yùn)行示例,
$ python src/pybind/cpp_thread_callback/tick_test.py run beg elapsed: 0 ms elapsed: 500 ms elapsed: 1000 ms run end
結(jié)語
Go coding!
總結(jié)
到此這篇關(guān)于pybind11: C++ 工程提供 Python 接口的文章就介紹到這了,更多相關(guān)pybind11: C++ 工程如何提供 Python 接口內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
標(biāo)準(zhǔn)CSV格式的介紹和分析以及解析算法實(shí)例詳解
這篇文章主要介紹了標(biāo)準(zhǔn)CSV格式的介紹和分析以及解析算法實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2016-12-12
C語言實(shí)現(xiàn)二叉樹的搜索及相關(guān)算法示例
這篇文章主要介紹了C語言實(shí)現(xiàn)二叉樹的搜索及相關(guān)算法,結(jié)合具體實(shí)例形式分析了基于C語言創(chuàng)建、遍歷、搜索等相關(guān)算法與實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-06-06
C語言實(shí)現(xiàn)圖的鄰接矩陣存儲(chǔ)操作
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)圖的鄰接矩陣存儲(chǔ)操作,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08

