pybind11 学习笔记
pybind11 学习笔记
- 0. 一个例子
- 1. 官方文档
- 1.1 Installing the Library
- 1.1.1 Include as A Submodule
- 1.1.2 Include with PyPI
- 1.1.3 Include with Conda-forge
- 1.2 First Steps
- 1.2.1 Separate Files
- 1.2.2 `PYBIND11_MODULE()` 宏
- 1.2.3 example.`cpython-38-x86_64-linux-gnu`.so 的名称由来
- 1.3 Build Systems
- 1.3.1 原始的调用方式
- 1.3.2 Modules with CMake
- 1.3.2.1 关于包名 package name
- 1.3.2.2 关于版本
- 1.3.2.3 Python 环境设置
- 1.3.2.4 静态链接与动态链接
- 1.3.3 其他方式 setuptools 等
- 1.3.3.1 find_package vs. add_subdirectory
0. 一个例子
如果直接去看官方文档的话, 不熟悉 C/C++ 的初学者可能会一头雾水, 因为它只描述了导入 pybind11 库的方法, 且比较分散(让你点击链接到另一个网页查看), 没有展示一个系统的例子.
故而去 B 站搜索相关演示视频, 找到了《如何在Python中调用C++代码?pybind11极简教程》. 过程如下:
- 创建一个名为
VSPyBind11Test
的文件夹, 并在 VS Code 中打开, 为项目的根目录; - 在根目录下创建文件夹
extern
, 终端命令进入extern
, 执行命令:
git clone --recursive -b v2.12 --single-branch https://github.com/pybind/pybind11.git
- 下载好后,
extern
下有一个pybind11
文件夹, 就是pybind11
库; - 创建
CMakeLists.txt
和example.cpp
文件, 写入内容:
cmake_minimum_required(VERSION 3.10)
project(VSPyBind11Test)# 可能会需要, 如果配置了环境变量就不需要了
# 或者你想在特定 Python 环境下的编译(还是比较重要的)
# set(PYTHON_EXECUTABLE /root/Miniconda/bin/python)
# set(PYTHON_INCLUDE_DIRS /root/Miniconda/include/python3.8)add_subdirectory(extern/pybind11)
pybind11_add_module(example example.cpp)
#include <pybind11/pybind11.h>
namespace py = pybind11;int add(int a, int b)
{return a + b;
}PYBIND11_MODULE(example, m)
{m.doc() = "Example module";m.def("add", &add, "Add two integers");
}
- 在根目录下创建
build
文件夹, 并在终端进入, 执行命令cmake ..
, 然后make
, 就会生成一个叫example.cpython-38-x86_64-linux-gnu.so
的文件, 就是导出的可供 python 调用的包; - 在
build
目录下, 创建demo.py
, 写入import example; example.add(1, 2)
, 执行python demo.py
, 则输出3
.
初步注解:
add_subdirectory(extern/pybind11)
可能是 C/C++ 开发导入第三方库的一种方法, 有了它就可以在 C/C++ 项目中使用pybind11
的内容了;PYBIND11_MODULE(example, m)
中,example
是导出的 python 模块名,m
是模块引用, 用来定义模块内容;- 从生成的
example.cpython-38-x86_64-linux-gnu.so
命名中可以看到, 它是依赖于 python 环境的, 我的默认 python 确实是 python3.8. 如果在CMakeLists.txt
中设置 python 环境为 python3.6:
set(PYTHON_EXECUTABLE /root/Miniconda/envs/py36/bin/python)
set(PYTHON_INCLUDE_DIRS /root/Miniconda/envs/py36/include/python3.6)
则得到的文件是 example.cpython-36m-x86_64-linux-gnu.so
.
- 注意, 在 python3.6 下生成的
so
文件不能在 python3.8 下运行, 反之亦然.
在 VS Code 下, 即使你构建了项目, 点开
example.cpp
, 可能依然是这样的
重启一下 VS Code 就好了. 而 CLion 下只需要点击 Reload CMake Project 就好了.
1. 官方文档
之后, 再看官方文档会好很多.
1.1 Installing the Library
从 Library 得知, 它就是个库. 官方推荐的安装方式有三种: submodule, PyPI, conda-forge.
1.1.1 Include as A Submodule
就是上面例子中 CMakeLists.txt 文件中的 add_subdirectory(extern/pybind11)
. 之所以说官方文档不够清楚, 当看到:
时, 试了一下:
# add_subdirectory(extern/pybind11)
include_directories(/extern/ptbind11/include)
根本不行: Unknown CMake command "pybind11_add_module".
点击 see Build systems, 看到一份 CMake 代码:
cmake_minimum_required(VERSION 3.15...3.29)
project(example LANGUAGES CXX)set(PYBIND11_FINDPYTHON ON)
find_package(pybind11 CONFIG REQUIRED)pybind11_add_module(example example.cpp)
install(TARGETS example DESTINATION .)
把上面例子中的 CMakeLists.txt 文件内容换成这样, 会报错:
Could not find a package configuration file provided by "pybind11" with any of the following names:pybind11Config.cmakepybind11-config.cmake
即使在 find_package(...)
中写明路径:
find_package(pybind11 CONFIG REQUIRED PATHS /root/CuProjects/VSPyBind11Test/extern/pybind11)
[小结]: 对于 Include as A Submodule, 暂时只能用 add_subdirectory(extern/pybind11)
了.
1.1.2 Include with PyPI
文档只告诉你可以通过 pip 安装 pybind11:
pip install pybind11
说这是一个标准的 Python 包格式: This will provide pybind11 in a standard Python package format. 然后, 如果 you want pybind11 available directly in your environment root, you can use:
pip install "pybind11[global]"
这种安装方式会往 /usr/local/include/pybind11 和 /usr/local/share/cmake/pybind11 中添加文件, 所以官方不推荐这种安装方式. 也就是说, 这样安装 pybind11 可以让你在项目的根目录中直接使用它.
[注意] 最新的 pybind11-v2.13 要求 python >=3.7, 如果是老版本的 python, 请安装 v2.12.
??? 然后呢? 执行完 pip 安装后, 我该如何在 C 项目中导入 pybind11 呢? 文档再一次导向 Build systems. 既然已经安装了, 我们删除 extern/pybind11
, 使用 find_package(pybind11 CONFIG REQUIRED)
, 继续用所给的 CMake 代码, 执行 cmake ..
命令, 和上面一样, 报找不到错误. 那看看下面怎么说:
呃, 看不懂了, 不知道这个 pyproject.toml 是个啥文件, 从文件名和上下文看, 它应该是一个构建 Python 包所需要的配置文件, 为了确认它是什么, 以及它和上面的 CMakeLists.txt 文件是什么关系, 先问问通义千问: [pyproject.toml 是什么?]
pyproject.toml 是一个用于 Python 项目的配置文件,它主要用于存储与构建系统和依赖管理相关的元数据。这个文件格式最初是为了简化 Python 包的构建过程而引入的,现在已经被广泛接受,并成为 PEP 518 和 PEP 517 标准的一部分。这些标准定义了如何使用现代工具来构建 Python 包,而无需依赖传统的 setup.py 文件。
那么这样就清楚了, 它跟 pybind11 似乎关系不大, 这里可能就是为了说明如何构建 Python 包, 而不是说明如何在 C 项目中导入 pybind11. 继续往下面看, Modules with meson-python, Modules with setuptools, … 都在讲如何构建 Python 包. 那么至此, 难以找到 “pip install pybind11 后如何导入到 C 项目中” 的答案. 耗费了一天时间, 试了很多方案, 都没能解决.
不过功夫不负有心人, 迷迷糊糊试了一下:
find_package(pybind11 CONFIG REQUIRED PATHS /root/Miniconda/lib/python3.8/site-packages/pybind11/share/cmake/pybind11)
这个路径是前面报错中所说的找不到的 pybind11Config.cmake, pybind11-config.cmake
两个文件的路径, 而 /root/Miniconda/lib/python3.8/site-packages/pybind11
是 pybind11
的 pip 安装路径. 竟然成功地导入了. 可以正常地将 C 语言导出 Python 接口了.
于是想, 何必找那么细呢? 只给出 pybind11 的安装路径不行吗? 于是:
find_package(pybind11 CONFIG REQUIRED PATHS /root/Miniconda/lib/python3.8/site-packages/pybind11)
find_package(pybind11 CONFIG REQUIRED PATHS /root/Miniconda/lib/python3.8/site-packages)
都是可以的. 奇了怪了, 当初 find_package(pybind11 CONFIG REQUIRED PATHS extern/pybind11)
咋就不行呢? 后来还是被我发现了, 后面会讲.
1.1.3 Include with Conda-forge
虽然不太清楚 conda 安装和 pip 安装的具体区别, 但猜测这种方式和 pip 应该是很类似的:
conda install -c conda-forge pybind11
文档只给了这么多. 为了测试这种安装方式, 在执行 conda 安装之前先卸载掉 pip 安装的 pybind11:
pip uninstall pybind11
卸载掉之后的, 发现重新构建 CMake 项目就找不到 pybind11 了. conda 安装 pybind11 之后, 再试试 cmake ..
, 哎! 成功了! 重磅! 用 conda 安装的 pybind11, 不用指定路径, 只需要:
find_package(pybind11 CONFIG REQUIRED)
这可能是因为用 conda 安装时, /root/Miniconda/include
和 /root/Miniconda/share/cmake
目录下均出现了 pybind11
文件夹, 其中 /root/Miniconda/share/cmake/pybind11
中有那两个之前找不到的文件. 甚至, 我在 conda 的 bin 目录下发现了一个叫 pybind11-config
的文件, 在终端执行:
pybind11-config --version
能输出:
2.13.5
也就是说, conda 安装 pybind11 时配置好了路径. 怎么配置的? 终端执行:
pybind11-config -h
# >>>>>>>>>>>> output >>>>>>>>>>>>
usage: pybind11-config [-h] [--version] [--includes] [--cmakedir] [--pkgconfigdir]
optional arguments:-h, --help show this help message and exit--version Print the version and exit.--includes Include flags for both pybind11 and Python headers.--cmakedir Print the CMake module directory, ideal for setting -Dpybind11_ROOT in CMake.--pkgconfigdir Print the pkgconfig directory, ideal for setting $PKG_CONFIG_PATH.
pybind11-config --includes
-I/root/Miniconda/include/python3.8 -I/root/Miniconda/lib/python3.8/site-packages/pybind11/includepybind11-config --cmakedir
/root/Miniconda/lib/python3.8/site-packages/pybind11/share/cmake/pybind11pybind11-config --pkgconfigdir
/root/Miniconda/lib/python3.8/site-packages/pybind11/share/pkgconfig
配置原来都在这, 难怪能直接 find_package(...)
. 但是, 如果你在 CLion 中构建 CMake 项目, 似乎还是会报找不到 pybind11 的错误.
conda 不仅仅是一个 python 管理工具!!!
- 记得当初需要用 R 语言处理一些数据时, 就有人说能用 conda 安装 R 语言, 我尝试了一下, 还是不错的;
- 在打开
scikit-build-core
的网页时, 发现很多程序已经用scikit-build-core
构建并发布到 PyPI 上了, cmake 和 ninja 赫然在列, 能用 pip 安装, 那可能也能用 conda 安装, 终端执行conda install cmake
, 果然安装了 cmake-3.26.4, 虽然 PyPI 上已经 3.30.3 了.- 终端运行
cmake --version
, 输出3.26.4
; 执行conda deactivate
回到非 conda 环境, 输出3.10.2
, 是系统上的 cmake 版本; 在其他 conda 环境(如我的 py36)下, 也是3.10.2
. 所以, conda 安装的软件是依赖于当前环境的.[小结]: conda 环境更像是一个镶嵌在操作系统上的虚拟系统, 它有自己的 bin, include, lib, sbin, share, 安装软件时很像 apt; 它是独立的, 又能访问宿主系统.
有了上面的分析, 可能的原因就是我们在终端或者 VS Code 中构建 CMake 项目时, 用的是 conda 的 base 环境, 而 CLion 不是. 执行 conda deactivate
回到系统环境, 再执行 cmake ..
, 果然找不到 pybind11
了. 现在, 我的 base 环境下是有 cmake-3.26.4 的, 设置 CLion 的 cmake 为 /root/Miniconda/bin/cmake
, 果然又行得通了; 在系统的非 conda 终端中执行 /root/Miniconda/bin/cmake ..
也行得通. 验证了 conda 环境像是一个镶嵌在操作系统上的虚拟系统的说法.
至此, 安装和导入部分算是完成了!
1.2 First Steps
这一节没什么好说的, 基本和第 0 节的例子差不多. 值得一提的是:
也就是说, <pybind11/pybind11.h>
要写在第一行.
1.2.1 Separate Files
In practice, implementation and binding code will generally be located in separate files.
既然文档提了这一句, 那就试试看:
VSPyBind11Test/
|----CMakeLists.txt
|----build/
|----src/
| |----example.cpp
| |----add/
| | |----add.h
| | |----add.cpp
| |----sub/
| | |----sub.h
| | |----sub.cpp
#include <pybind11/pybind11.h>
#include "add.h"
#include "sub.h"namespace py = pybind11;PYBIND11_MODULE(example, m)
{m.doc() = "Example module";m.def("add", &add, "Add two integers");m.def("sub", &sub, "Sub two integers");
}
编辑 CMakeLists.txt:
cmake_minimum_required(VERSION 3.10...3.29)
project(example LANGUAGES CXX)include_directories(src/add src/sub)set(PYBIND11_FINDPYTHON ON)
find_package(pybind11 CONFIG REQUIRED)add_library(add SHARED src/add/add.cpp)
add_library(sub SHARED src/sub/sub.cpp)pybind11_add_module(example src/example.cpp)
target_link_libraries(example PRIVATE add sub)install(TARGETS example DESTINATION .)
则完成了多文件的链接编译. 这样的话, 如果需要导出现有的 C 函数, 只需要编写一个类似 example.cpp
的文件, 然后进行链接就可以了.
在这个 CMakeLists.txt 文件中, 你也可以这样写:
...
find_package(pybind11 CONFIG REQUIRED)
pybind11_add_module(example src/example.cpp src/add/add.cpp src/sub/sub.cpp)
install(TARGETS example DESTINATION .)
1.2.2 PYBIND11_MODULE()
宏
PYBIND11_MODULE(example, m)
{m.doc() = "Example module";m.def("add", &add, "Add two integers");m.def("sub", &sub, "Sub two integers");
}
上面代码中的 PYBIND11_MODULE()
是一个宏, 接收两个参数, example
是 module name
, 它大概应该和 example.cpp
一致, 试了一下 PYBIND11_MODULE(example1, m)
, 会发现 import example,
import example1
都报错. m
是一个指向 example
模块的 py::module_
类型的变量, 它是可变的, 如 mm
也可以.
关于此宏的其他功能, 可见官方文档, 这里就不多说了.
1.2.3 example.cpython-38-x86_64-linux-gnu
.so 的名称由来
文档中还提到:
$ c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)
这是手动编译的命令, 执行 python-config --extension-suffix
, 会根据你的 python 解释器和系统得到:
.cpython-38-x86_64-linux-gnu.so
Python 解释器是可以设置的, CLion 中, 只要在设置中指定好解释器就好了. 当然也可以在 CMakeLists.txt 中直接设置:
set(PYTHON_EXECUTABLE /root/Miniconda/bin/python)
关于这一点, 上面的例子已经提过了.
1.3 Build Systems
当明白上面讲的内容后, 就会发现文档的这一节是在教你如何在使用 pybind11 导出 C 程序的 Python 接口的情况下, 构建 Python 包. 有多种方式, 包括 Modules with CMake, Modules with meson-python, Modules with setuptools, Building with cppimport, Building with CMake, …
(可以先了解一下 Python 包的构建, 前面关于 pyproject.toml
的事就清楚了.)
1.3.1 原始的调用方式
按理说, 像前面那样构建好 .so
文件就够了, 已经可以在 python 脚本中进行导入和调用:
import example
print(example.add(1, 2)) # 3
可移植性又好, 想在哪里调用就直接 cmake
一下. 因为在一个环境下编译的 .so
在另一个环境不一定可用.
不好的地方在于, 开发环境 IDE 会提示你找不到 example
, 因为这里没有指导 IDE 进行代码检查的 Python 代码信息. 解决办法是靠 example.pyi
, 它对代码的执行不产生任何影响, 只对 IDE 的代码检查及文档说明提供帮助:
def add(x: int, y: int) -> int:"""Add two integers:param x: The first integer:param y: The second integer:return: The result of x + y"""...def sub(x: int, y: int) -> int:"""Integer Subtraction Computation:param x: The first integer:param y: The second integer:return: The result of x - y"""...
这样, 不光 IDE 不报错, 还能提供漂亮的文档提示.
1.3.2 Modules with CMake
有了对 Python 包构建 的了解, 就彻底明白 pyproject.toml
是怎么回事:
[build-system] # 使用 scikit-build-core 后端构建 python 包
requires = ["scikit-build-core", "pybind11"]
build-backend = "scikit_build_core.build"[project]
name = "example"
version = "0.1.0"
但我现在只知道官方给的 Python 包构建的例子, 并不知道 scikit-build-core
作为后端时, 怎样将 CMake 项目构建成 Python 包. 于是, 跟着导航, 导向 scikit_build_example 看一看:
scikit_build_example/
├── LICENSE
├── README.md
├── CMakeLists.txt
├── pyproject.toml
├── src/
│ ├── main.cpp
│ └── scikit_build_example/
│ └── __init__.py
└── tests/
项目的目录结构(忽略了一些可选部分), 和 Packaging Python Projects 中给的例子差不多, 多了个 CMakeLists.txt
, example.py
换成了 main.cpp
. 具体内容就不列出来了, 请参考 Github. 同样是执行:
pip install .
# or
python -m build
就构建好了, 安装之后, 就可以正常使用了.
1.3.2.1 关于包名 package name
pyproject.toml
中的
[project]
# 生成的包名, pip 安装时使用的包名(xxx.dist-info), 但 import 时需要使用模块名(安装的代码所在地)
name = "example" # 本来是 scikit_build_example, 为了研究包名, 更改为 example
是指 pip 安装时的名称, 也就是 pip install .
后, 你如果要卸载, 就要用这个名字, 因为 site-packages
下, 包信息的文件夹是 example.dist-info
; 它也是你执行 python -m build
时, 生成的 whl
包的名字.
install(TARGETS _core DESTINATION cmake_example) # .so 文件所在地
这是 CMakeLists.txt
中的安装语句, 表示生成的 _core.so
文件会安装到 site-packages
文件夹下的哪里, 这里会安装到 cmake_example
文件夹下.
src/
├── main.cpp
└── scikit_build_example/ # 这个名字要跟 name = "..." 中的一致, 不然这个 __init__.py 不会被安装└── __init__.py
我估计是有了 name = "example"
之后, 构建工具会去 src
下寻找同名的 Python 包, 把它复制到 conda 环境的 site-packages
下. 而 C 包 xxx.so
是 Python 包所需要的, 所以, CMakeLists.txt 中的 DESTINATION
要考虑为: Python 包需要我在哪里!
1.3.2.2 关于版本
project(${SKBUILD_PROJECT_NAME}VERSION ${SKBUILD_PROJECT_VERSION}LANGUAGES CXX
)
其中的 SKBUILD_PROJECT_NAME
变量是
[project]
name = "example"
version = "0.0.1"
当你手动执行 cmake ..
时, 这两个值似乎都是空的, 当 python -m build
时, 应该是 scikit-build-core
执行的 cmake
命令, 并将 name = "example", version = "0.0.1"
传递给了 CMake.
CMake 获取版本信息后:
target_compile_definitions(_core PRIVATE VERSION_INFO=${PROJECT_VERSION})
中又有版本信息, 这应该是传递给 cpp 代码的, 因为我发现:
#ifdef VERSION_INFOm.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO);
#elsem.attr("__version__") = "dev";
#endif
在检测宏 VERSION_INFO
, 正是 CMake 中的 VERSION_INFO
, 把它删掉:
target_compile_definitions(_core PRIVATE)
执行 example.__version__
就输出 'dev'
. 设置成:
target_compile_definitions(_core PRIVATE VERSION_INFO=0.2.0)
执行 example.__version__
就输出 '0.2.0'
.
1.3.2.3 Python 环境设置
在 CMakeLists.txt 中, 可能使用以下语句设置 Python:
set(PYTHON_BASE_PATH /root/Miniconda/envs/xxx)
set(PYTHON_INCLUDE_DIRS ${PYTHON_BASE_PATH}/include/python3.8)
set(PYTHON_LIBRARY ${PYTHON_BASE_PATH}/lib/libpython3.8.so)
set(PYTHON_EXECUTABLE ${PYTHON_BASE_PATH}/bin/python)
但似乎这样更简单:
set(PYTHON_BASE_PATH /root/Miniconda/envs/xxx)
find_package(Python REQUIRED COMPONENTS Interpreter Development.Module PATHS ${PYTHON_BASE_PATH})# Interpreter 对应了
# set(PYTHON_EXECUTABLE ${PYTHON_BASE_PATH}/bin/python)
# 还能获取 Python_VERSION
# Development.Module 对应了
# set(PYTHON_INCLUDE_DIRS ${PYTHON_BASE_PATH}/include/python3.x)
# set(PYTHON_LIBRARY ${PYTHON_BASE_PATH}/lib/libpython3.x.so)
注意这个需要 CMake-3.12+(3.15+ recommended, 3.18.2+ ideal).
1.3.2.4 静态链接与动态链接
问题来了: 当按照 1.2.1 Separate Files 中的文件结构时, 安装 Python 包后就会报错:
ImportError: libadd.so: cannot open shared object file: No such file or directory
而 1.2.1 中的手动编译, 不打包 Python 包就不报错. 检查了一下安装后的包解构, 也没啥问题:
example/
├── __init__.py
├── _core.cpython-38-x86_64-linux-gnu.so
├── libadd.so
├── libsub.so
└── __pycache__/└── __init__.cpython-38.pyc
到底咋回事, cd 到包里面, 执行:
(base) root@kklt:~/CuProjects/CLionPyBind11Test# cd ~/Miniconda/lib/python3.8/site-packages/example/
(base) root@kklt:~/Miniconda/lib/python3.8/site-packages/example# ls
__init__.py __pycache__ _core.cpython-38-x86_64-linux-gnu.so libadd.so libsub.so
(base) root@kklt:~/Miniconda/lib/python3.8/site-packages/example# python
Python 3.8.18 (default, Sep 11 2023, 13:40:15)
[GCC 11.2.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import _core
Traceback (most recent call last):File "<stdin>", line 1, in <module>
ImportError: libadd.so: cannot open shared object file: No such file or directory
在包内调用也不行; 把自己编译的 _core.cpython-38-x86_64-linux-gnu.so
移动到 example 内:
(base) root@kklt:~/Miniconda/lib/python3.8/site-packages/example# cp ~/CuProjects/CLionPyBind11Test/cmake-build-debug/_core.cpython-38-x86_64-linux-gnu.so ./
(base) root@kklt:~/Miniconda/lib/python3.8/site-packages/example# python
Python 3.8.18 (default, Sep 11 2023, 13:40:15)
[GCC 11.2.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import _core
>>> _core.add(1, 2)
3
甚至 example
包也能正常导入了:
(base) root@kklt:~# python
Python 3.8.18 (default, Sep 11 2023, 13:40:15)
[GCC 11.2.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import example
>>> example.add(1, 2)
3
不清楚什么原因, 可能是 Python 包构建工具经过了什么特殊处理导致的. 还有一种解决办法:
add_library(add STATIC src/add_module/add.cpp)
add_library(sub STATIC src/sub_module/sub.cpp)
SHARED
换成 STATIC
也可以, 此时甚至不需要 labadd.so
和 labadd.so
了. 问了通义千问:
对于 ImportError: libadd.so: cannot open shared object file: No such file or directory, 我把 add_library(add SHARED src/add/add.cpp) 改成 add_library(add STATIC src/add/add.cpp) 就解决了错误, 这是为什么?
你遇到的 ImportError: libadd.so: cannot open shared object file: No such file or directory 错误通常是因为 Python 在运行时找不到必要的共享库。当你将 add_library(add SHARED src/add/add.cpp) 改为 add_library(add STATIC src/add/add.cpp) 后,问题得到了解决,原因在于静态库和共享库在链接时的行为不同。
=> 共享库 (SHARED)
当使用 add_library(… SHARED …) 创建共享库时,生成的库文件(如 libadd.so)是一个独立的动态链接库。这意味着:
---- 动态链接:在编译时,链接器不会将库文件中的符号(函数和变量)嵌入到最终的可执行文件或库中。相反,它会在运行时查找这些符号。
---- 运行时依赖:运行时,Python 解释器需要找到并加载这个共享库。因此,共享库必须存在于 Python 解释器可以找到的地方(通常是 LD_LIBRARY_PATH 或 /etc/ld.so.conf 指定的路径)。
=> 静态库 (STATIC)
当使用 add_library(… STATIC …) 创建静态库时,生成的库文件(如 libadd.a)包含了所有必要的符号。这意味着:
---- 静态链接:在编译时,链接器会将静态库中的符号嵌入到最终的可执行文件或库中。这样做的结果是最终的可执行文件或库包含了静态库中的所有符号。
---- 无需运行时依赖:由于所有的符号都已经嵌入到了最终的可可执行文件或库中,因此在运行时不需要再查找和加载静态库。
但, 终究是不知道为啥构建的 Python 包会找不到就在跟前的 libadd.so
.
1.3.3 其他方式 setuptools 等
构建 Python 包的方式还有很多, 且似乎大有不同, 比如 setuptools
安装的包似乎找不到 .so
文件在哪, 且构建文件目录中竟然没有 CMakeLists.txt 问价, 故而不明白 C 项目是如何构建的.
内容太多且用不到, 只作为大概了解, 故而就不进一步追究了. 但在下文中还是看到了一点东西可以解答前面的疑惑:
1.3.3.1 find_package vs. add_subdirectory
对于基于 CMake 的 C 项目, 没有内部包含 pybind11 repository, 所以要进行 external installation, 再通过 find_package(pybind11)
查找.
find_package(pybind11 REQUIRED)
pybind11_add_module(example example.cpp)
注意: find_package(pybind11)
只有在 pybind11 被正确地安装到系统中后才能正确工作, 即, 下载或克隆后:
# Classic CMake
cd pybind11
mkdir build
cd build
cmake ..
make install
这是常规的安装软件的步骤, 所以, 前面说的"用 find_package(...)
替换 add_subdirectory
"不工作.
找到了 pybind11 包之后, 前面提到的 pybind11_add_module
就可以使用了. 当你定义了 PYBIND11_FINDPYTHON
:
set(PYBIND11_FINDPYTHON ON)
或者执行 cmake
命令行时加上: DPYBIND11_FINDPYTHON=ON
, 那么 pybind11 会自动为你执行 FindPython
操作.
相关文章:

pybind11 学习笔记
pybind11 学习笔记 0. 一个例子1. 官方文档1.1 Installing the Library1.1.1 Include as A Submodule1.1.2 Include with PyPI1.1.3 Include with Conda-forge 1.2 First Steps1.2.1 Separate Files1.2.2 PYBIND11_MODULE() 宏1.2.3 example.cpython-38-x86_64-linux-gnu.so 的…...

36.贪心算法3
1.坏了的计算器(medium) . - 力扣(LeetCode) 题目解析 算法原理 代码 class Solution {public int brokenCalc(int startValue, int target) {// 正难则反 贪⼼int ret 0;while (target > startValue) {if (target % 2 0…...

htop(1) command
文章目录 1.简介2.格式3.选项4.交互式命令5.示例6.小结参考文献 1.简介 htop 是一种交互式、跨平台的基于 ncurses 的进程查看器。 类似于 top,但 htop 允许您垂直和水平滚动,并使用指向设备(鼠标)进行交互。您可以观察系统上运行的所有进程࿰…...
ODrive学习——添加485编码器支持
系列文章目录 文章目录 系列文章目录前言一、端口处理二、在Encoder中引入新的类型1.增加485类型2.增加串口的初始化操作3.数据处理 总结 前言 尝试在ODrive中添加485型的编码器的支持 一、端口处理 计划使用PA2及PA3作为485通信的端口。这样首先要把外部温度传感器的I/O口给…...
在OSX上: 使用brew安装nginx 后,在通过编译安装第三方模块
1. 下载nginx安装包nginx: download 2. mac环境编译源码需要安装pcre、openssl等第三方依赖,可参考在OSX上: 使用brew安装nginx 后,在通过编译安装第三方模块 - ZhYQ_note - 博客园 3. nginx可支持的配置参考Building nginx from Sources 4. 执行 ./…...

C++初阶学习第六弹------标准库中的string类
目录 一.标准库中的string类 二.string的常用接口函数 2.1string类对象的构造 2.2 string的容量操作 2.3 string类的访问与遍历 2.4 string类对象的修改 2.5 string类常用的非成员函数 三、总结 一.标准库中的string类 可以简单理解成把string类理解为变长的字符数组&#x…...

Linux基础-Makefile的编写、以及编写第一个Linux程序:进度条(模拟在 方便下载的同时,更新图形化界面)
目录 一、Linux项目自动化构建工具-make/Makefile 编辑 背景: makefile小技巧: 二、Linux第一个小程序-进度条 先导: 1.如何利用/r,fflush(stdout)来实现我们想要的效果; 2.写一个倒计时: 进度条…...

华为云DevSecOps和DevOps
目录 1.华为云DevSecOps和DevOps 1.1 DevSecOps 1.1.1 核心功能 1.1.2 优势 1.2 DevOps 1.2.1 核心功能 1.2.2 优势 1.3 DevOps和DevSecOps的区别 1.3.1 安全性集成 1.3.2 自动化的安全工具 1.3.3 团队协作 1.3.4 质量与合规性 1.3.5 成本与风险管理 1.3.5 总结 …...
RESTful API设计中的GET与POST:何时及为何成为首选?
RESTful API设计中的GET与POST:何时及为何成为首选? 在RESTful API的设计过程中,HTTP方法(GET、POST、PUT、DELETE等)作为资源的操作标识,扮演着至关重要的角色。然而,在实际开发中,…...
秋招自我介绍
1min 尊敬的面试官您好,感谢您百忙之中参加我的面试。我是来自北京大学的王峰。 在实习经历方面,我曾在美团和小米公司实习。在美团主要负责核身、质检、词云项目。 在核身项目中,完整的经历从0-1的项目上线过程 在质检项目中,进…...

html加载页面
<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>算数模一体化</title> </head><b…...

【数据可视化】Arcgis api4.x 热力图、时间动态热力图、timeSlider时间滑块控件应用 (超详细、附免费教学数据、收藏!)
1.效果 目录 1.效果 2.安装配置 3.热力图 4.TimeSlider滑块应用 4.1 时间滑块控件 4.2 添加控件 5.时间动态热力图 2.安装配置 这里不教大家如何在前端框架使用arcgis api。不过npm安装、css如何引入、教学数据存放与图层加载的教程,可以浏览我之前发的一篇文…...

WEB攻防-JavaWweb项目JWT身份攻击组件安全访问控制
知识点: 1、JavaWeb常见安全及代码逻辑; 2、目录遍历&身份验证&逻辑&JWT; 3、访问控制&安全组件&越权&三方组件; 演示案例: JavaWeb-WebGoat8靶场搭建使用 安全问题-目录遍历&身份认…...

【C++算法】模拟算法
替换所有的问号 题目链接 替换所有的问号https://leetcode.cn/problems/replace-all-s-to-avoid-consecutive-repeating-characters/description/ 算法原理 代码步骤 class Solution { public:string modifyString(string s) {int n s.size();for(int i 0; i < n; i){…...

模版进阶(template)
1.非类型模版参数 模版参数分类类型形参与非类型形参。 ① 类型形参:出现在在模板参数列表中,跟在class或者typename之类的参数类型名称。 ② 非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当…...

vue2与vue3的区别
1.v-if与v-for的优先级不同 2.vue2中存在数据更新以后视频不更新的问题,故存在$set来解决这一问题,而vue3中数据双向绑定不存在数据更新视图不更新的问题,所以也就没有this.$set...

借助大模型将文档转换为视频
利用传统手段将文档内容转换为视频,比如根据文档内容录制一个视频,不仅需要投入大量的时间和精力,而且往往需要具备专业的视频编辑技能。使用大模型技术可以更加有效且智能化地解决上述问题。本实践方案旨在依托大语言模型(Large …...

UE5安卓项目打包安装
Android studio安装 参考:https://docs.unrealengine.com/5.2/zh-CN/how-to-set-up-android-sdk-and-ndk-for-your-unreal-engine-development-environment/ 打开android studio的官网:Download Android Studio & App Tools - Android Developers …...

MSF的使用学习
一、更新MSF apt update # 更新安装包信息;只检查,不更新(已安装的软件包是否有可用的更新,给出汇总报告) apt upgrade # 更新已安装的软件包,不删除旧包; apt full-upgrade # 升级包&#x…...

C++ —— 关于vector
目录 链接 1. vector的定义 2. vector的构造 3. vector 的遍历 4. vector 的扩容机制 5. vector 的空间接口 5.1 resize 接口 5.2 push_back 5.3 insert 5.4 erase 5.5 流插入与流提取 vector 并不支持流插入与流提取,但是可以自己设计,更…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...

【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...

在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...

关于easyexcel动态下拉选问题处理
前些日子突然碰到一个问题,说是客户的导入文件模版想支持部分导入内容的下拉选,于是我就找了easyexcel官网寻找解决方案,并没有找到合适的方案,没办法只能自己动手并分享出来,针对Java生成Excel下拉菜单时因选项过多导…...

【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL
ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...

热烈祝贺埃文科技正式加入可信数据空间发展联盟
2025年4月29日,在福州举办的第八届数字中国建设峰会“可信数据空间分论坛”上,可信数据空间发展联盟正式宣告成立。国家数据局党组书记、局长刘烈宏出席并致辞,强调该联盟是推进全国一体化数据市场建设的关键抓手。 郑州埃文科技有限公司&am…...