pybind11介绍

x33g5p2x  于2022-02-19 转载在 其他  
字(9.2k)|赞(0)|评价(0)|浏览(238)

**      pybind11****是一个轻量级的仅头文件库,主要用于创建现有C++代码的Python绑定**,它的源码在https://github.com/pybind/pybind11,license为BSD,最新发布版本为2.9.1。

**     **可将pybind11库视为Boost.Python的一个小型自包含版本(Think of this library as a tiny self-contained version of Boost.Python),其中剥离了与绑定生成无关的所有内容。**pybind11依赖于python(2.7或3.5+)****和C++**标准库

**      pybind11核心功能**:pybind11可以将以下C++功能映射到Python

**     **(1).函数接受和返回自定义数据结构(per value, reference, or pointer);

**     **(2).实例方法和静态方法;

**     **(3).重载函数;

**     **(4).实例属性和静态属性;

**     **(5).任意异常类型;

**     **(6).枚举;

**     **(7).回调;

**     **(8).迭代器和范围(ranges);

**     **(9).自定义运算符;

**     **(10).单继承和多继承;

**     **(11).STL数据结构;

**     **(12).具有引用计数的智能指针如std::shared_ptr;

**     **(13).具有正确引用计数的内部引用(Internal references with correct reference counting);

**     **(14).可以在Python中扩展具有虚(和纯虚)方法的C++类。

**      pybind11支持的编译器**:

**     **(1).Clang/LLVM 3.3或更高版本;

**     **(2).GCC 4.8或更高版本:注:编译2.9.1版本的test,GCC需要5以上版本

**     **(3).Visual Studio 2015或更高版本。

**      pybind11的安装**:

**     **(1).通过conda,执行:conda install -c conda-forge pybind11

**     **(2).通过pip,执行:pip install pybind11

**     **更详细文档介绍参考:https://pybind11.readthedocs.io/en/stable/index.html

**     **以下为测试代码:

**     **include/funset.hpp:C++代码头文件

#ifndef PYBIND11_FUNSET_HPP_
#define PYBIND11_FUNSET_HPP_

#include <string>

#define PYBIND11_API __attribute__((visibility ("default")))

PYBIND11_API int get_random_number(int min, int max);

struct PYBIND11_API Pet {
    Pet(const std::string& name) : name_(name) { }
    void setName(const std::string& name) { name_ = name; }
    const std::string& getName() const { return name_; }

    static int getAge() { return 18; }

    std::string name_;
};

struct Dog : Pet {
    Dog(const std::string& name) : Pet(name) { }
    std::string bark() const { return "woof!"; }
};

struct PolymorphicPet {
    virtual ~PolymorphicPet() = default;
};

struct PolymorphicDog : PolymorphicPet {
    std::string bark() const { return "woof!"; }
};

struct Pet2 {
    Pet2(const std::string& name, int age) : name_(name), age_(age) { }

    void set(int age) { age_ = age; }
    void set(const std::string& name) { name_ = name; }
    int getAge() const { return age_; }
    const std::string& getName() const { return name_; }

    std::string name_;
    int age_;
};

struct Widget {
    int foo(int x, float y) { return 0; };
    int foo(int x, float y) const { return 1; };
};

struct Pet3 {
    enum Kind {
        Dog = 0,
        Cat
    };

    struct Attributes {
        float age = 0;
    };

    Pet3(const std::string& name, Kind type) : name_(name), type_(type) { }

    std::string name_;
    Kind type_;
    Attributes attr_;
};

#endif // PYBIND11_FUNSET_HPP_

**     **src/funset.cpp:C++代码的实现,编译生成动态库或静态库

#include "funset.hpp"
#include <random>
#include <iostream>

PYBIND11_API int get_random_number(int min, int max)
{
    fprintf(stdout, "get random number through std::uniform_int_distribution\n");
    std::random_device rd;
    std::mt19937 generator(rd());
    std::uniform_int_distribution<int> distribution(min, max);
    return distribution(generator);
}

**     **example.cpp:生成python接口,编译生成动态库,若Python为3.8,则动态库名字为:example.cpython-38-x86_64-linux-gnu.so

#include <pybind11/pybind11.h>
#include <funset.hpp>

// reference: https://pybind11.readthedocs.io/en/stable/

namespace py = pybind11;

// If you prefer the py::overload_cast syntax but have a C++11 compatible compiler only,
// you can use py::detail::overload_cast_impl with an additional set of parentheses
template <typename... Args>
using overload_cast_ = pybind11::detail::overload_cast_impl<Args...>;

// #define PYBIND11_MODULE(name, variable): name: module name; variable: a variable of type `py::module_` which can be used to initialize the module
PYBIND11_MODULE(example, m) {
	m.doc() = "pybind11 example plugin"; // optional module docstring

	// 1. bindings for a simple function
	m.def("get_random_number", &get_random_number, "A function that get random number", py::arg("min"), py::arg("max"));

	// 2. bindings for a custom C++ data structure: class or struct
	// class_ creates bindings for a C++ class or struct-style data structure
	py::class_<Pet>(m, "Pet")
		 // init() is a convenience function that takes the types of a constructor’s parameters as template arguments and wraps the corresponding constructor
		.def(py::init<const std::string &>())
		.def("setName", &Pet::setName)
		.def("getName", &Pet::getName)

		// Static member functions can be bound in the same way using class_::def_static()
		.def_static("getAge", &Pet::getAge)

		// 3. Binding lambda functions
		.def("__repr__",
			[](const Pet &a) {
				return "<example.Pet named '" + a.name_ + "'>";
			}
		)

		// We can also directly expose the name_ field using the class_::def_readwrite() method.
		// A similar class_::def_readonly() method also exists for const fields
		.def_readwrite("name", &Pet::name_);

	// 4. class inheritance
	// There are two different ways of indicating a hierarchical relationship to pybind11:
	// the first specifies the C++ base class as an extra template parameter of the class_:
	py::class_<Dog, Pet /* <- specify C++ parent type */>(m, "Dog")
		.def(py::init<const std::string &>())
		.def("bark", &Dog::bark);

	// // Alternatively, we can also assign a name to the previously bound Pet class_ object and reference it when binding the Dog class
	// py::class_<Pet> pet(m, "Pet");
	// pet.def(py::init<const std::string &>())
   	// 	.def_readwrite("name", &Pet::name);

	// // Method 2: pass parent class_ object:
	// py::class_<Dog>(m, "Dog", pet /* <- specify Python parent type */)
	// 	.def(py::init<const std::string &>())
	// 	.def("bark", &Dog::bark);

	// 5. class polymorphic
	// In C++, a type is only considered polymorphic if it has at least one virtual function and pybind11 will automatically recognize this
	py::class_<PolymorphicPet>(m, "PolymorphicPet");
	py::class_<PolymorphicDog, PolymorphicPet>(m, "PolymorphicDog")
		.def(py::init<>())
		.def("bark", &PolymorphicDog::bark);

	// Again, return a base pointer to a derived instance
	// Given a pointer to a polymorphic base, pybind11 performs automatic downcasting to the actual derived type
	m.def("pet_store2", []() { return std::unique_ptr<PolymorphicPet>(new PolymorphicDog); });

	// 6. Overloaded methods
	py::class_<Pet2>(m, "Pet2")
		.def(py::init<const std::string &, int>())
		.def("set", static_cast<void (Pet2::*)(int)>(&Pet2::set), "Set the pet's age")
		.def("set", static_cast<void (Pet2::*)(const std::string &)>(&Pet2::set), "Set the pet's name")
		.def("getAge", &Pet2::getAge)
		.def("getName", &Pet2::getName);

	// If you have a C++14 compatible compiler, you can use an alternative syntax to cast the overloaded function
	// py::class_<Pet2>(m, "Pet2")
	// 	.def("set", py::overload_cast<int>(&Pet2::set), "Set the pet's age")
	// 	.def("set", py::overload_cast<const std::string &>(&Pet2::set), "Set the pet's name");

	// If a function is overloaded based on constness, the py::const_ tag should be used
	py::class_<Widget>(m, "Widget")
		.def("foo_mutable", overload_cast_<int, float>()(&Widget::foo))
		.def("foo_const",   overload_cast_<int, float>()(&Widget::foo, py::const_));

	// 7. Enumerations and internal types: nested types
	py::class_<Pet3> pet(m, "Pet3");

	pet.def(py::init<const std::string &, Pet3::Kind>())
		.def_readwrite("name", &Pet3::name_)
		.def_readwrite("type", &Pet3::type_)
		.def_readwrite("attr", &Pet3::attr_);

	py::enum_<Pet3::Kind>(pet, "Kind")
		.value("Dog", Pet3::Kind::Dog)
		.value("Cat", Pet3::Kind::Cat)
		.export_values();

	py::class_<Pet3::Attributes>(pet, "Attributes")
		.def(py::init<>())
		.def_readwrite("age", &Pet3::Attributes::age);
}

**     **build.sh:编译cpp文件生成动态库

#! /bin/bash

real_path=$(realpath $0)
dir_name=`dirname "${real_path}"`
echo "real_path: ${real_path}"
echo "dir_name: ${dir_name}"

# build funset static library
# g++ -O3 -Wall -static -c -std=c++11 src/funset.cpp -Iinclude
# ar -r libfunset.a funset.o

# build funset dynamic library
g++ -O3 -Wall -shared -fPIC -std=c++11 -o libfunset.so -c src/funset.cpp -Iinclude

g++ -O3 -Wall -shared -std=c++11 -fPIC $(python3-config --includes) example.cpp \
    -o example$(python3-config --extension-suffix) \
    -L./ -lfunset \
    -I../../src/pybind11/include \
    -Iinclude

# # delete funset library, example.cpython-38-x86_64-linux-gnu.so has contained relevant export symbols
rm libfunset*

python test.py

**     **test.py:测试导出的python接口

import example

print("\n1.test simple function")
print("random number:", example.get_random_number(min=1, max=100))

print("\n2.test class:")
p = example.Pet("Molly")
print("name:", p.getName())
p.setName("Charly")
print("name:", p.getName())
print("age:", p.getAge())
print("name:", p.name)
p.name = "Molly"
print("name:", p.name)

print("\n3.test lambda function")
print("p:", p)

print("\n4.test inheritance class:")
p = example.Dog("Molly")
print("name:", p.name)
print("bark:", p.bark())

print("\n5.test polymorphic class:")
p = example.pet_store2()
print("type:", type(p))
print("bark:", p.bark())

print("\n6.test overload methods:")
p = example.Pet2("Molly", 10)
p.set("Charly")
p.set(18)
print("name:", p.getName())
print("age:", p.getAge())

print("\n7.test nested types")
p = example.Pet3("Lucy", example.Pet3.Cat)
print(f"type: {p.type}, value: {int(p.type)}")
print("Kind members:", p.Kind.__members__)

**     **执行结果如下:

**     ** GitHub:https://github.com/fengbingchun/Python_Test 

相关文章