在centos7中利用pybind11构建C++的动态库供python调用

需求:在 CentOS 7 中,使用 pybind11 构建 C++ 的动态库供 Python 调用涉及几个步骤:安装依赖、获取 pybind11、编写绑定代码、编译动态库,以及在 Python 中调用。以下是详细的步骤:

步骤 1:安装依赖

首先,确保系统中安装了 Python、pip 和 C++ 编译环境:

sudo yum install epel-release
sudo yum update
sudo yum install gcc-c++ python3 python3-pip python3-devel

接着,使用 pip 安装 pybind11:

pip3 install pybind11

步骤 2:获取 pybind11 头文件

通过 pip 安装 pybind11 后,你可以在 Python 站点包目录中找到 pybind11 的头文件。使用以下命令查找 pybind11 的安装位置:

pip3 show pybind11 | grep Location

记下头文件的路径,你将在编译时需要它。

步骤 3:编写绑定代码

创建一个 C++ 文件(例如 example.cpp),并使用 pybind11 编写绑定代码。下面是一个简单的例子:

/*--------------------------------------编译一个类--------------------------------*/
#include <pybind11/pybind11.h>
#include <iostream>
using namespace std;

#include "Combiner.h"

class Calculator {
public: 
    void test(double W, double H)
    {
        vector<map<double, vector<Line>>> combine_lines;
        map<double, vector<Line>> angle_lines;

        vector<Line> tmp_lines_0;
        tmp_lines_0.push_back(Line(Point(0, 0), Point(0, 10)));
        tmp_lines_0.push_back(Line(Point(0, 10), Point(20, 10)));
        tmp_lines_0.push_back(Line(Point(20, 10), Point(20, 0)));
        tmp_lines_0.push_back(Line(Point(20, 0), Point(0, 0)));
        angle_lines[0] = tmp_lines_0;

        vector<Line> tmp_lines_90;
        tmp_lines_90.push_back(Line(Point(0, 0), Point(0, -20)));
        tmp_lines_90.push_back(Line(Point(0, -20), Point(-10, -20)));
        tmp_lines_90.push_back(Line(Point(-10, -20), Point(-10, 0)));
        tmp_lines_90.push_back(Line(Point(-10, 0), Point(0, 0)));
        angle_lines[90] = tmp_lines_90;

        combine_lines.push_back(angle_lines);
        Combiner combinber(W, H);
        tuple<bool, vector<tuple<double, double, double>>> res = combinber.combine_with_rotate(combine_lines);
        bool flag = std::get<0>(res); //访问bool值
        std::vector<std::tuple<double, double, double>>& vec = std::get<1>(res); //访问vector

        // 确保vector不为空
        if (!vec.empty()) {
            // 访问并打印vector中的第一个tuple
            const auto& firstTuple = vec.front(); // 或vec[0]
            cout << "First tuple: ("
                << std::get<0>(firstTuple) << ", "
                << std::get<1>(firstTuple) << ", "
                << std::get<2>(firstTuple) << ")" << endl;
        }
        else {
            std::cout << "The vector is empty." << std::endl;
        }
    }
};
namespace py = pybind11;
PYBIND11_MODULE(combine_with_rotate, m) 
{
    py::class_<Calculator>(m, "Calculator")
        .def(py::init<>()) // 注册构造函数
        .def("test", &Calculator::test); // 注册test成员函数
}

/*--------------------------------------编译一个纯C函数--------------------------------*/
#include <pybind11/pybind11.h>

int add(int a, int b) {
    return a + b;
}

PYBIND11_MODULE(example, m) {
    m.def("add", &add, "A function which adds two numbers");
}

步骤 4:编译动态库

使用 c++ 命令手动编译动态库。你需要指定 Python 包含的目录、pybind11 包含的目录以及输出的动态库名称。根据你的 Python 版本和 pybind11 的安装位置,命令可能有所不同。以下是一个编译示例:

c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)

这条命令将生成一个动态库(例如 example.cpython-36m-x86_64-linux-gnu.so),可以被 Python 导入。

步骤 5:在 Python 中调用

在同一目录下,启动 Python 解释器,然后导入你的模块并调用函数:

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

如果一切正常,这将输出 3

编译命令解释

c++ -O3 -Wall -shared -std=c++11 -fPIC ( p y t h o n 3 − m p y b i n d 11 − − i n c l u d e s ) e x a m p l e . c p p − o e x a m p l e (python3 -m pybind11 --includes) example.cpp -o example (python3mpybind11includes)example.cppoexample(python3-config --extension-suffix)

  • c++:调用 C++ 编译器。
  • -O3:这是编译器的优化选项,-O3 代表进行高级优化。
  • -Wall:开启所有警告消息,帮助开发者发现潜在的问题。
  • -shared:生成动态链接库(shared library,或称为动态库)。
  • -std=c++11:使用 C++11 标准进行编译。
  • -fPIC:生成位置无关代码(Position Independent Code),这对于动态链接库是必需的。
  • $(python3 -m pybind11 --includes):这部分命令用于自动获取 pybind11 的头文件路径,以及 Python 的头文件路径。这是为了让编译器知道 pybind11 和 Python 头文件的位置。$(...) 是 Bash 中的命令替换,意味着先执行括号内的命令,然后将输出替换到当前位置。
  • example.cpp:你的源文件,包含了要绑定的 C++ 代码。
  • -o example$(python3-config --extension-suffix):指定输出文件的名称。$(python3-config --extension-suffix) 用于获取 Python 扩展模块的文件扩展名,确保生成的动态库与当前 Python 版本兼容。

注意

  • 确保在编译命令中使用正确的 Python 版本和 pybind11 路径。
  • 如果你的项目较大,考虑使用 CMake 来管理构建过程。pybind11 提供了支持 CMake 的示例和工具。

通过遵循这些步骤,你可以在 CentOS 7 上使用 pybind11 构建 C++ 的动态库,并在 Python 中调用它。

步骤 4:编译动态库可以用另一种简单的方法

方法二:也可以使用setup.py来编译和安装使用pybind11的C++的扩展模块。这种方法可以更加方便地集成到Python的包管理和分发系统中。

from setuptools import setup
from pybind11.setup_helpers import Pybind11Extension, build_ext

# 定义扩展模块,指定源代码文件
ext_modules = [
    Pybind11Extension(
        'combine_with_rotate',  # 模块名
         ['main.cpp', 'src/clipper.cpp', 'src/Combiner.cpp', 'src/fileProcess.cpp', 'src/Point.cpp'],  # 源文件列表
         include_dirs=['include'],  # 包含头文件的目录,指向include目录
         libraries=['curl'],  # 要链接的库

        # 这里可以指定额外的编译参数,但对于Pybind11Extension通常不必要
        cxx_std=17,  # 指定C++标准为C++17
    ),
]

setup(
    name='combine_with_rotate',
    version='0.1',
    description='A sample Python package with C++ extension',
    ext_modules=ext_modules,  # 包含定义的扩展模块
    cmdclass={"build_ext": build_ext},  # 指定build_ext命令
)

这个命令将编译你的 C++ 代码,并将生成的扩展模块安装到你的 Python 环境中,使你可以像导入普通 Python 模块一样导入它,该命令适用于部署,因为它将模块安装到 Python 的 site-packages 目录,这使得模块即使在源代码被修改或删除后也仍然可用。。

python3 setup.py install

开发和测试时,建议使用下面的命令

python3 setup.py build_ext --inplace

步骤 5:在 Python 中调用

在同一目录下,启动 Python 解释器,然后导入你的模块并调用函数:

import combine_with_rotate

calc = combine_with_rotate.Calculator()
calc.test(100, 200)
print("Good Lucky To You!")

setup.py通用模板

from setuptools import setup, Extension
from pybind11.setup_helpers import Pybind11Extension, build_ext

# 定义包含目录和库目录
include_dirs = [
    "path/to/headers",  # 头文件目录,如果你的 jsoncpp 头文件不在标准路径
    "path/to/examplelib/headers",  # examplelib 的头文件目录
    # 其他必要的头文件目录
]
library_dirs = [
    "path/to/library",  # jsoncpp 库文件目录,如果库不在标准路径
    "path/to/examplelib/library",  # examplelib 库文件目录
    # 其他库文件目录
]

ext_modules = [
    Pybind11Extension(
        "my_extension",  # 扩展模块的名字
        ["binding.cpp", "class1.cpp", "class2.cpp"],  # 需要编译的源文件列表
        include_dirs=include_dirs,  # 指定头文件目录
        library_dirs=library_dirs,  # 指定库文件目录
        libraries=["jsoncpp", "examplelib"],  # 指定需要链接的库名
        # 这里还可以添加其他编译选项,如 define_macros, extra_compile_args 等
    ),
]

setup(
    name="my_extension",
    version="0.1",
    author="Your Name",
    description="A pybind11 example with multiple C++ files, header directories, and linking to jsoncpp",
    long_description="",
    ext_modules=ext_modules,
    cmdclass={"build_ext": build_ext},
    zip_safe=False,
)

注意事项

  • 头文件和库文件的位置:确保你正确地指定了 jsoncpp 头文件和库文件的位置。如果你通过包管理器(如 aptyumbrew)安装 jsoncpp,库文件和头文件可能已经在标准路径中了,这种情况下你可能不需要设置 include_dirslibrary_dirs
  • 库名:在 libraries 列表中,你只需要指定库的名称而不是具体的文件名。例如,对于 libjsoncpp.sojsoncpp.lib,你只需指定 "jsoncpp"
  • 跨平台构建:当你在不同平台上构建扩展时,可能需要根据操作系统调整库名称或路径。
  • 依赖关系:如果 jsoncpp 依赖其他库,你可能还需要链接这些依赖库。这可以通过在 libraries 列表中添加更多库名来实现。

更新GCC编译器

C++17标准需要较新版本的GCC编译器(至少是GCC 7)。您可以通过以下步骤更新GCC编译器:

  • 查看当前GCC版本

    gcc --version
    
  • CentOS 7安装更新的GCC版本: CentOS 7的默认仓库中可能不包含最新版本的GCC,但您可以通过Software Collections (SCL)库来安装更新版本的GCC。首先,安装SCL:

    sudo yum install centos-release-scl
    

    然后,安装较新版本的GCC(例如,安装GCC 9):

    sudo yum install devtoolset-9-gcc devtoolset-9-gcc-c++
    

    激活新版本的GCC:

    source /opt/rh/devtoolset-9/enable
    

    这将只为当前终端会话启用新版本的GCC。您可能希望将这个命令添加到您的.bashrc或其他shell启动脚本中,以便每次启动新终端时自动激活。

  • 确认更新后的GCC版本

    gcc --version
    

    确保显示的版本支持C++17(GCC 7或更高)。

要在 CentOS 7 中安装 Python 3.8.0,并确保携带了 _ctypes 模块

  1. 安装依赖项:

    在安装 Python 3.8.0 之前,确保你的系统已安装了编译 Python 所需的依赖项。在 CentOS 7 上,你可以运行以下命令安装这些依赖项:

    sudo yum install gcc openssl-devel bzip2-devel libffi-devel
    
  2. 下载 Python 3.8.0 源代码:

    访问 Python 官方网站,下载 Python 3.8.0 的源代码压缩包(tar.gz 格式)。

    wget https://www.python.org/ftp/python/3.8.0/Python-3.8.0.tgz
    
  3. 解压并编译安装 Python:

    解压下载的 Python 源代码并进入解压后的目录:

    tar -zxvf Python-3.8.0.tgz
    cd Python-3.8.0
    

    然后,配置、编译并安装 Python:

    ./configure --enable-optimizations --with-ctypes
    make
    sudo make altinstall
    

    make altinstall 命令会将 Python 安装为 python3.8,以避免覆盖系统默认的 Python 版本。

  4. 验证安装:

    安装完成后,你可以使用以下命令验证新安装的 Python 版本和是否携带了 _ctypes 模块:

    python3.8 --version
    python3.8 -c "import _ctypes; print(_ctypes)"
    

    如果输出显示了 Python 3.8.0 的版本信息和 _ctypes 模块相关信息,则说明安装成功。

通过以上步骤,你应该能够在 CentOS 7 中成功安装 Python 3.8.0,并确保携带了 _ctypes 模块。

如果你使用的是 Python 3.8.0 版本,那么应该使用 pip3.8 命令来安装 Pybind11,而不是 pip3。因为 pip3 命令可能会安装到系统默认的 Python 版本,而不是你安装的 Python 3.8.0。

因此,正确的安装命令应该是:

bashCopy code
pip3.8 install pybind11

这样可以确保将 Pybind11 安装到你指定的 Python 3.8.0 版本中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值