python调用C++方法

python调用C++方法

以下是主要的python绑定cpp的方法:

方法年份代表用户
适用于 CPython 的 C/C++ 扩展模块1991标准库
PyBind11(推荐用于 C++)2015
Cython(推荐用于 C)2007gevent、kivy
HPy2019
mypyc2017
ctype2003oscrypto
cffi2013cryptography、pypy
SWIG1996crfsuite
Boost.Python2002
cppyy2017

其中

  1. ctypes: C 与 Python 绑定, Python 内建模块,通过调用.so动态链接库来使用方法,不需要在c++中有特殊的写法,如果使用c++则需要使用extern c的方式再包装一层
  2. Boost.Python: C++ 与 Python 绑定, Boost 模块
  3. pybind11: C++11 与 Python 绑定, 减去了旧 C++ 支持,更轻量化,需要在编写cpp时处理响应的绑定函数。
  4. CPython python标准库中的方式,需要手写绑定方法

一般使用pybind11多一些,因为更为轻量化,能够轻易地加入c++特性。

一、背景

现有个项目需要封装给python用户使用,讨论过后决定使用pybind11进行封装。

二、使用pybind11为python添加c++扩展

​ 首先在安装时确保c++环境和python环境保持一致。

​ 在已经完整安装pybind11的基础上,在c++项目中添加pybind11库,接下来就是使用pybind11编写c++到pybind11的扩展:

1、首先导入pybind11的头文件:
#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
2、添加方法绑定
PYBIND11_MODULE( 模块名, 模块实例对象 ){
    m.doc() = "pybind11 example";   //可选,说明这个模块是做什么的
    //封装的具体操作。这些操作包括普通的函数的封装,类的访问等下面用不同例子来说明问题
}
调用普通函数

pybind11的模块实例对象提供了 def()函数,用来封装普通的函数,具体的用法为

def( "给python调用方法名"&实际操作的函数, "函数功能说明" ). //其中函数功能说明为可选

例子如下:

#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>

int mytest( int i, int j ){
    ....
    ....
    ....
}

PYBIND11_MODULE( py2cpp, m ){
    m.doc() = "pybind11 example";
    m.def("mytest", &mytest, "just test" );
}

//在python中使用 模块名.函数名 来访问
//例如本例子为  py2cpp.mytest(1,2)

对应在python中的访问:

import py2cpp

	py2cpp.mytest(1,2)

调用类
pybind11::class_<命名空间::类名>(m, "在python中构造这个类的方法名" )
        .def(pybind11<>::init())            //构造器,对应的是c++类的构造函数,如果没有这个构造函数,或者参数对不是会调用失败
        .def( "python中函数名", &命名空间::类名::函数名 );

调用命名空间外的类:

#include <pybind11/pybind11.h>

class Hello
{
public:
    Hello(){}
    void say( const std::string s ){
        std::cout << s << std::endl;
    }
};

PYBIND11_MODULE( py2cpp, m ){
    m.doc() = "pybind11 example";

    pybind11::class_<Hello>(m, "Hello" )
        .def(pybind11::init())
        .def( "say", &Hello::say );
}
//python 调用方式
//1, 先通过构造器来构建实例,方法为 模块名.构造器名
//2,调用对应的方法, 模块名.方法名
//例如本例子需要如下调用
// c=py2cpp.Hello()
// c.say()

调用命名空间中的对象:

#include <pybind11/pybind11.h>

namespace NS{
class World{
public:
    World(){}
    void say( const std::string s ){
        std::cout << s << std::endl;
    }
};

PYBIND11_MODULE( py2cpp, m ){
    m.doc() = "pybind11 example";
    pybind11::class_<NS::World>(m, "World")
        .def(pybind11::init())
        .def("say", &NS::World::say);
}

//本例子需要如下调用
// c=py2cpp.World()
// c.say()

注意把c++文件编译成.so的编译命令:

g++ -shared -std=c++11 -fPIC `python3 -m pybind11 --includes` 要编译的源代码 -o 模块名`python3-config --extension-suffix` -I /path/to/python3

注意:

在使用pybind11过程中涉及3个地方用模块名,且必须一致,否则会出错:

  1. c++代码中 PYBIND11_MODULE 后面的
  2. 编译命令行中的模块名,参见上面
  3. 在python中import时使用到的模块名
使用cmake进行模块编译

在实际项目中关联的c++文件会很多,建议使用cmake进行编译,最终生成相应模块。

cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
project("my_xxx")

#----------------------------CMAKE & GLOBAL PROPERTIES-------------------------#
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
include_directories(/usr/local/include/opencv4)

###============= C++11 support====================================
add_definitions(-std=c++11)
option(CUDA_USE_STATIC_CUDA_RUNTIME OFF)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_BUILD_TYPE Debug)
if(${CMAKE_VERSION} VERSION_LESS "3.1")
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if (COMPILER_SUPPORTS_CXX11)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
elseif (COMPILER_SUPPORTS_CXX0X)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
else ()
    message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif ()
else()
	set(CMAKE_CXX_STANDARD 11)
	set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()

#=============== Find Packages ====================================
find_package(.....)
include_directories(......)
FIND_LIBRARY(.....)

add_subdirectory(src/pybind11-2.1.1)
pybind11_add_module(my_xxx xxxx.cpp)

如果想在已有 C++ 动态库上扩展 pybind11 绑定,那么 target_link_libraries 链接该动态库就可以了。

target_link_libraries(my_xxx PUBLIC xxxx)

参考:

python调用c++利器–pybind11 - 知乎 (zhihu.com)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值