PyBind11 基础绑定
为了高效,现代化的绑定C++代码到Python,PyBind11框架是一个非常流行的选择。它提供了简洁的语法和强大的功能,使得C++与Python之间的交互变得简单而高效。
安装 PyBind11
在开始之前,确保你已经安装了PyBind11。
1. 使用Pip安装
安装到当前环境:
bash
pip install pybind11
安装到系统全局(可被CMAKE
直接搜索):
bash
pip install "pybind11[global]"
2. 手动配置
- 手动克隆PyBind11仓库:
bash
git clone https://github.com/pybind/pybind11.git
- 在 CMake 项目中引入 PyBind11:
cmake
add_subdirectory({path}/pybind11)
编写 CMakeLists.txt
创建一个 CMakeLists.txt
文件来配置你的项目。以下是一个基本的示例:
cmake
cmake_minimum_required (VERSION 3.11)
project(PYTHON_TEST)
if(MSVC)
add_compile_options(/utf-8 /EHsc)
endif()
# 启用pybind11的Python自动查找功能,避免人为指定路径。
set(PYBIND11_FINDPYTHON ON)
# 查找pybind11包(pip install "pybind11[global]" 后才能直接 find_package)
find_package(pybind11 CONFIG REQUIRED)
# 如果手动配置了pybind11则可以直接使用 无需find_package
# add_subdirectory({path}/pybind11)
# 创建Python扩展模块(该函数由pybind11提供,本质是add_library的封装)
# 生成的 testLib 会是 testLib.pyd (Windows) 或 testLib.so (Linux)
pybind11_add_module(testLib main.cpp)
# 可选:直接安装到 Python site-packages (个人不推荐这种污染全局环境的行为)
# install(TARGETS testLib DESTINATION ${Python_SITEARCH})
set_property(TARGET testLib PROPERTY CXX_STANDARD 20)
编写绑定代码
创建一个 main.cpp
文件,编写你的C++代码并使用PyBind11进行绑定。
cpp
#include <iostream>
#include <pybind11/pybind11.h>
namespace py = pybind11; // 简化命名空间
// 一个简单的C++函数
static int add(int a, int b)
{
return a + b;
}
// Python有特定的入口函数搜索规则:文件名必须与模块名相同
// 使用 PYBIND11_MODULE 宏来简化并定义模块
PYBIND11_MODULE(testLib, m)
{
m.doc() = "这是一个绑定测试库"; // 模块的__doc__文档属性
// pybind11通过模板元编程技术自动识别类型信息并生成绑定代码完成绑定
m.def("add", &add, "基础函数测试");
}
以上代码编译后将生成的.pyd文件放置在python项目内/配置到解释器环境库中即可使用。
- 代码测试
python
import testLib # 确保可以搜索到目标pyd文件
print(testLib.add(10000, 2000)) # -> 正常输出计算结果
STD类型绑定
PyBind11支持直接绑定许多标准库类型,以下是一个绑定std::string
和std::vector<int>
的示例:
cpp
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h> // 支持 std::string, std::vector 等STD类型转换
namespace py = pybind11;
// 计算并返回命名空间部分字符串
static std::string getNameSpace(const std::string& str)
{
// pybind11会自动解析Python字符串并构造std::string传入(产生至少一次拷贝)
auto pos = str.find(':');
if (pos != std::string::npos)
{
return str.substr(0, pos);
}
return "";
}
// 按空格拆分字符串为单词列表
static std::vector<std::string> splitBySpace(const std::string& str)
{
std::vector<std::string> result;
std::istringstream iss(str);
std::string token;
while (iss >> token)
{
result.push_back(token);
}
// pybind11会自动将std::vector解析并构造为Python的list返回
return result;
}
PYBIND11_MODULE(testLib, m)
{
m.doc() = "这是一个绑定测试库";
m.def("getNameSpace", &getNameSpace, "命名空间计算");
m.def("splitBySpace", &splitBySpace, "字符串拆分");
// 除了直接绑定函数指针,还可以绑定lambda对象
m.def("greet", [] (const std::string& name)
{
return "Hello, " + name + "!";
},
"一个简单的问候函数");
}
默认形参处理
PyBind11支持为绑定的函数指定默认参数值。以下是一个示例:
cpp
#include <iostream>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
namespace py = pybind11;
static void testArgs(int a, int b = 10, const std::string& msg = "默认参数")
{
std::cout << "a: " << a << ", b: " << b << ", msg: " << msg << "\n";
}
PYBIND11_MODULE(testLib, m)
{
m.doc() = "这是一个绑定测试库";
// 使用 py::arg 指定参数名和默认值 由pybind11自动处理
m.def("testArgs", &testArgs, py::arg("a"), py::arg("b") = 10, py::arg("msg") = "默认参数", "函数参数测试");
}
异常处理
PyBind11允许你在C++代码中抛出异常,并将其转换为Python异常。以下是一个示例:
cpp
#include <iostream>
#include <pybind11/pybind11.h>
#include <functional>
namespace py = pybind11;
static void testErr1()
{
throw std::runtime_error("抛出异常,pybind11会捕获并转换为Python异常");
}
// pybind11可以直接将Python函数转换为std::function传入C++
static void testErr2(const std::function<void()>& func)
{
try
{
func(); // 调用传入的Python函数
}
catch (const std::exception& e)
{
std::cerr << "捕获到Python异常: " << e.what() << "\n";
}
}
PYBIND11_MODULE(testLib, m)
{
m.doc() = "这是一个绑定测试库";
m.def("testErr1", &testErr1, "CPP抛出异常测试");
m.def("testErr2", &testErr2, "CPP捕获Python异常测试");
}
总结
PyBind11是一个强大且易于使用的C++绑定库,适合将C++代码集成到Python项目中。通过上述示例,你可以快速上手并创建自己的Python扩展模块。更多高级功能和用法,请参考官方文档。