pybind11: C++ 工程如何提供 Python 接口

获取 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

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 编译就完成了。

示例代码

first_steps.h

first_steps.cc

first_steps_pb.cc

绑定一个类

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

#include

#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;

using TickRunCallback = std::function;

class Tick {

public:

  using clock = std::chrono::high_resolution_clock;

  Tick(std::int64_t tick_ms,

      std::int64_t life_ms = std::numeric_limits::max());

  Tick(TickEvent tick_event, std::int64_t tick_ms,

      std::int64_t life_ms = std::numeric_limits::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 &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

#include

#include

#include

#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_>(m, "Tick")

    .def(py::init())

    .def(py::init

                  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());

}

编译出动态库后,把路径添加进 PYTHONPATH:

export PYTHONPATH=:$PYTHONPATH

# 依赖其他动态库的话,把路径添加进 LIBRARY_PATH

# Linux

export LD_LIBRARY_PATH=:$LD_LIBRARY_PATH

# macOS

export DYLD_LIBRARY_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

示例代码

tick.h

tick.cc

tick_test.cc

tick_pb.cc

tick_test.py

运行示例代码

获取代码,

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

深圳网站建设www.sz886.com

你可能感兴趣的:(pybind11: C++ 工程如何提供 Python 接口)