ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Python using C++ library with Pybin11

2022-05-21 03:31:08  阅读:146  来源: 互联网

标签:__ Python py library SimpleNum print using example def


Python using C++ library with Pybin11

0 Why pybind11?

上文介绍了Py-Boost的搭建和使用Py-Boost build on win10 with MSVC,这篇文章介绍的pybind11与Boost::Python一样可以作为C++ => Python的桥梁。不同的是Boost太重了,引用pybin11官方说法"Boost is an enormously large and complex suite of utility libraries that works with almost every C++ compiler in existence", 这带来的代价就是,Boost::Python里有很多不必要、不实用的代码,因此pybind11这类轻量级(仅头文件库只需要大约 4K 行代码)就有了用武之地。

1 build environment

  1. 安装pybind11
git clone https://github.com/pybind/pybind11.git
  1. 构建开发文件夹

pybind11同级目录下,构建如果工程文件结构

- root
- src
main.cpp
*.cpp
- inc
*.h
- pybind11
CMakeLists.txt
  1. 设置CMakeLists.txt
cmake_minimum_required(VERSION 3.4...3.18)
project(example)

# C++ 17
set(CMAKE_CXX_STANDARD 17)

# include
set(INCLUDE_H ${CMAKE_CURRENT_SOURCE_DIR}/inc)
include_directories(${INCLUDE_H})

# src
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src DIR_SRCS)

# pybind11
add_subdirectory(pybind11) # 包含子目录 pybind11 https://github.com/pybind/pybind11.git
pybind11_add_module(example ${DIR_SRCS})

# version setup
set(EXAMPLE_VERSION_INFO 1.0) # 设定版本号
target_compile_definitions(example
PRIVATE VERSION_INFO=${EXAMPLE_VERSION_INFO})

2 C++开发 - Python使用

以下全部代码见examplePybind

2.1 say hello

say hellopybind11官方文档中一个hello world测试案例

#include <pybind11/pybind11.h>

#define STRINGIFY(x) #x
#define MACRO_STRINGIFY(x) STRINGIFY(x)

namespace py = pybind11;

int add(int i, int j) // add func, pybind hello world.
{
return i + j;
}

PYBIND11_MODULE(example, m) {
m.doc() = R"pbdoc(
Pybind11 example plugin
-----------------------

.. currentmodule:: cmake_example

.. autosummary::
:toctree: _generate

add
subtract
)pbdoc"; // 说明文档

m.def("add", &add, R"pbdoc(
Add two numbers

Some other explanation about the add function.
)pbdoc"); // 绑定 add 函数

#ifdef VERSION_INFO // 版本号
m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO);
#else
m.attr("__version__") = "dev";
#endif
}

2.2 build py lib

采用CMake方式build

  • windows
mkdir build
cd build
cmake "Visual Studio 15 2022" -A x64 ..
cmake --build . --config Release -- /maxcpucount:8
  • osx
mkdir build
cd build
cmake ..
make -j8

build/Release/文件夹下可以找到example.pyd或者example.so文件

2.3 python import

>>> import example
>>> print(example.add(1, 2))
>>> 3

2.4 template function

当然仅仅只是跑通官方测试案例还是不过瘾的,下面对模板函数进行构建

main.cpp

template <typename T> // 模板函数
T mul(T x, T y)
{
return x * y;
}

PYBIND11_MODULE(example, m) {
m.doc() = R"pbdoc(
Pybind11 example plugin
-----------------------

.. currentmodule:: cmake_example

.. autosummary::
:toctree: _generate

add
subtract
)pbdoc";

m.def("mul", mul<int>, py::arg("x")=1, py::arg("y")=2); // 模板函数 int
m.def("mul", mul<float>, py::arg("x")=2.0, py::arg("y")=3.0); // 模板函数 float
m.def("mul", mul<double>, py::arg("x")=3.0, py::arg("y")=4.0); // 模板函数 double

#ifdef VERSION_INFO
m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO);
#else
m.attr("__version__") = "dev";
#endif
}

examplePybind.py

import example
print("example version: ", example.__version__)

def test_func():
print("mul() = {}".format(example.mul())) # 默认参数 int x, y = 1, 2
print("mul(12, 3) = {}".format(example.mul(12, 3))) # int x, y = 12, 3
print("mul(12.0, 2.0) = {}".format(example.mul(12.0, 2.0))) # float x, y = 12.0, 2.0

if __name__ == "__main__":
test_func()

结果

mul() = 2
mul(12, 3) = 36
mul(12.0, 2.0) = 24.0

2.5 template class

更进一步的,模板 + 类 + 分文件编写

分别支持intfloat类型的微型数值计算的类。

numeric.h

#include <pybind11/pybind11.h>

#define FLT_MAX 3.402823466e+38F
#define FLT_MIN 1.175494351e-38F

template <typename T>
class SimpleNum // 类声明
{
public:
T m_x;

SimpleNum();
SimpleNum(T x): m_x(x) {}
T Add(T y);
T Sub(T y);
T Mul(T y);
T Div(T y);
void Set(T x);
T Get();

private:
bool _IsZero(T y);

};

numeric.cpp

#include "../inc/numeric.h"

namespace py = pybind11;

template <typename T>
T SimpleNum<T>::Add(T y) // 具体实现
{
return this->m_x + y;
}

template <typename T>
T SimpleNum<T>::Sub(T y)
{
return this->m_x - y;
}

template <typename T>
T SimpleNum<T>::Mul(T y)
{
return this->m_x * y;
}

template <typename T>
T SimpleNum<T>::Div(T y)
{
if (_IsZero(y))
{
return FLT_MAX;
}
return this->m_x / y;
}

template <typename T>
void SimpleNum<T>::Set(T x)
{
this->m_x = x;
}

template <typename T>
T SimpleNum<T>::Get()
{
return this->m_x;
}

template <typename T>
bool SimpleNum<T>::_IsZero(T y)
{
return y == 0;
}

// 就地绑定, 会在main.cpp对class_ext也进行绑定
void class_ext(py::module &m) {
py::class_<SimpleNum<int> > (m, "SimpleNum")
.def(py::init<int>())
.def_readwrite("x", &SimpleNum<int>::m_x) // 并不建议直接访问成员变量x, 安全起见用Set(), Get()
.def("Add", &SimpleNum<int>::Add)
.def("Sub", &SimpleNum<int>::Sub)
.def("Mul", &SimpleNum<int>::Mul)
.def("Div", &SimpleNum<int>::Div)
.def("Set", &SimpleNum<int>::Set)
.def("Get", &SimpleNum<int>::Get);

py::class_<SimpleNum<float> > (m, "SimpleNumFLT")
.def(py::init<float>())
.def_readwrite("x", &SimpleNum<float>::m_x) // 并不建议直接访问成员变量x, 安全起见用Set(), Get()
.def("Add", &SimpleNum<float>::Add)
.def("Sub", &SimpleNum<float>::Sub)
.def("Mul", &SimpleNum<float>::Mul)
.def("Div", &SimpleNum<float>::Div)
.def("Set", &SimpleNum<float>::Set)
.def("Get", &SimpleNum<float>::Get);
}

main.cpp

void class_ext(py::module &m); // 分文件绑定,先声明,其他源文件会定义

PYBIND11_MODULE(example, m) {
m.doc() = R"pbdoc(
Pybind11 example plugin
-----------------------

.. currentmodule:: cmake_example

.. autosummary::
:toctree: _generate

add
subtract
)pbdoc";

class_ext(m); // 绑定class

#ifdef VERSION_INFO
m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO);
#else
m.attr("__version__") = "dev";
#endif
}

examplePybind.py

import example
print("example version: ", example.__version__)

def test_class():
num = example.SimpleNum(3) # 类 -> 对象
print("num = {}".format(num.x))
print("{} + 4 = {}".format(num.x, num.Add(4))) # 成员函数 Add
print("{} - 4 = {}".format(num.x, num.Sub(4))) # 成员函数 Sub
print("{} * 4 = {}".format(num.x, num.Mul(4))) # 成员函数 Mul
print("{} / 4 = {}".format(num.x, num.Div(4))) # 成员函数 Div
print("num.Set(99)")
num.Set(99) # 成员函数 设置成员变量x
print("num.Get(): {}".format(num.Get())) # Get函数 获取成员变量x

numFlt = example.SimpleNumFLT(30.0) # 类 -> 对象,float类型
print("numFlt = {}".format(numFlt.x))
print("{} + 4.0 = {}".format(numFlt.x, numFlt.Add(4.0)))
print("{} - 4.0 = {}".format(numFlt.x, numFlt.Sub(4.0)))
print("{} * 4.0 = {}".format(numFlt.x, numFlt.Mul(4.0)))
print("{} / 4.0 = {}".format(numFlt.x, numFlt.Div(4.0)))
print("numFlt.Set(199.0)")
numFlt.Set(199.0)
print("numFlt.Get(): {}".format(numFlt.Get()))

if __name__ == "__main__":
test_class()

结果

num = 3
3 + 4 = 7
3 - 4 = -1
3 * 4 = 12
3 / 4 = 0
num.Set(99)
num.Get(): 99
numFlt = 30.0
30.0 + 4.0 = 34.0
30.0 - 4.0 = 26.0
30.0 * 4.0 = 120.0
30.0 / 4.0 = 7.5
numFlt.Set(199.0)
numFlt.Get(): 199.0

2.6 C++ vector - Python list

C++中的vector与python list实际上可以做一个对应

实现一个针对数组的平方函数,支持intfloat类型

vectorTest.h

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/complex.h>
#include <pybind11/functional.h>
#include <pybind11/chrono.h>
#include <vector>

using namespace std;
namespace py = pybind11;

template <typename T>
vector<T> vecSquare(vector<T> vec);

vectorTest.cpp

#include "../inc/vectorTest.h"

template <typename T>
vector<T> vecSquare(vector<T> vec)
{
vector<T> ret;
for (int i=0; i < vec.size(); i++)
{
ret.emplace_back(vec[i] * vec[i]);
}
return ret;
}

// 就地绑定, 会在main.vector_ext
void vector_ext(py::module &m) {
m.def("Square", &vecSquare<int>);
m.def("Square", &vecSquare<float>);
m.def("Square", &vecSquare<double>);
}

examplePybind.py

def test_vector():
b = [1, 2, 3]
print("b^2 = {}".format(example.Square(b)))
c = [4.0, 5.0, 6.0]
print("c^2 = {}".format(example.Square(c)))

if __name__ == "__main__":
test_vector()

结果

b^2 = [1, 4, 9]
c^2 = [16.0, 25.0, 36.0]

标签:__,Python,py,library,SimpleNum,print,using,example,def
来源: https://www.cnblogs.com/XiWJ/p/16294303.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有