C++调用python(一)


一、基本使用方法
二、调用简单语句
三、调用函数
四、调用类
五、调用SSD目标检测算法
六、遇到的错误


最近训练一个3D分割的模型,需要将其结合到项目中,由于项目是C++开发,而这边python训练好的模型尝试了ONNX、libtorch等转换C++也没有成功,因此考虑采用C++直接调用python代码,这里对里面用到的一些方法做一个总结,方便以后查看。

一、基本使用方法

1.1 调用步骤

  • 将数据值从C/C++转换为Python
  • 使用转换后的值对Python接口例程执行函数调用
  • 将Python调用中的数据值转换为C/C++

1.2 编译链接

使用python提供的C/C++接口,需要包含python安装目录下的头文件Python.h 编译、链接时需要指定头文件、python库的地址。
示例:

include: /home/zjh/anaconda3/envs/learn/include/python3.6m   Python.h
lib: /home/zjh/anaconda3/envs/learn/lib/  libpython3.6m.a/libpython3.6m.so

注:如果需要Numpy库的话需要找到numpy的地址,一般存放于第三方库路径下,如:

/home/zjh/anaconda3/envs/learn/lib/python3.6/site-packages/numpy/core/include/numpy   arrayobject.h

1.3 基本接口

  • 解释器
    在C/C++调用python之前必须为其指定解释器环境。
void Py_Initialize():       
    初始化python解释器.C/C++中调用Python之前必须先初始化解释器
int Py_IsInitialized():
    返回python解析器的是否已经初始化完成,如果已完成,返回大于0,否则返回0
void Py_Finalize() :
    撤销Py_Initialize()和随后使用Python/C API函数进行的所有初始化,
    并销毁自上次调用Py_Initialize()以来创建并未被销毁的所有子解释器。
  • 格式转换
    在C/C++中,所有的Python类型都被声明为PyObject类型,为了让C/C++能够操作python的数据,python提供了C语言数据类型到PyObject类型的转换接口。

    • 数字/字符串
    PyObject* Py_BuildValue( const char *format, ...)
      Py_BuildValue()提供了类似c语言printf的参数构造方法,format是要构造的参数的类型列表,函数中剩余的参数即要转换的C语言中的整型、浮点型或者字符串等。
    其返回值为PyObject型的指针。
    

    format对应的类型参见官网, 如:

    s(str或None)[char *]
    使用'utf-8'编码将以null结尾的C字符串转换为Python str对象。如果C字符串指针为NULL,则表示None。
    
    i(int)[int]
    将普通的C int转换为Python整数对象。
    ...
    
    • 列表
    PyObject* PyList_New( Py_ssize_t len)
      创建一个新的Python列表,len为所创建列表的长度
    
    int PyList_SetItem( PyObject *list, Py_ssize_t index, PyObject *item)
      向列表中添加项。当列表创建以后,可以使用PyList_SetItem()函数向列表中添加项。 list:要添加项的列表。 index:所添加项的位置索引。 item:所添加项的值。
    
    PyObject* PyList_GetItem( PyObject *list, Py_ssize_t index)
      获取列表中某项的值。list:要进行操作的列表。index:项的位置索引。
    
    Py_ssize_t PyList_Size(PyObject * list)
      返回列表中列表对象的长度;这相当于列表对象上的 len(list) 。
    
    int PyList_Append( PyObject *list, PyObject *item)
    int PyList_Sort( PyObject *list)
    int PyList_Reverse( PyObject *list)
      Python/C API中提供了与Python中列表操作相对应的函数。例如
    列表的append方法对应于PyList_Append()函数。
    列表的sort方法对应于PyList_Sort()函数。
    列表的reverse方法对应于PyList_Reverse()函数。
    
    • 元组
    PyObject* PyTuple_New( Py_ssize_t len) 
      PyTuple_New()函数返回所创建的元组。其函数原型如下所示。len:所创建元组的长度。 
    
    int PyTuple_SetItem( PyObject *p, Py_ssize_t pos, PyObject *o) 
      当元组创建以后,可以使用PyTuple_SetItem()函数向元组中添加项。p:所进行操作的元组,pos:所添加项的位置索引,o:所添加的项值。
    
    PyObject* PyTuple_GetItem( PyObject *p, Py_ssize_t pos)
      可以使用Python/C API中PyTuple_GetItem()函数来获取元组中某项的值。p:要进行操作的元组,pos:项的位置索引
    
    Py_ssize_t PyTuple_Size(PyObject * p)
      获取指向元组对象的指针,并返回该元组的大小。
    
    int _PyTuple_Resize( PyObject **p, Py_ssize_t newsize) 
      当元组创建以后可以使用_PyTuple_Resize()函数重新调整元组的大小。其函数原型如下所示。p:指向要进行操作的元组的指针,newsize:新元组的大小
    
    • 字典
    PyObject* PyDict_New() 
      PyDict_New()函数返回所创建的字典。
    
    
    int PyDict_SetItem( PyObject *p, PyObject *key, PyObject *val) 
    int PyDict_SetItemString( PyObject *p, const char *key, PyObject *val) 
      当字典创建后,可以使用PyDict_SetItem()函数和PyDict_SetItemString()函数向字典中添加项。 其参数含义如下。
    p:要进行操作的字典。key:添加项的关键字,
    对于PyDict_SetItem()函数其为PyObject型,
    对于PyDict_SetItemString()函数其为char型,val:添加项的值。 
    
    
    PyObject* PyDict_GetItem( PyObject *p, PyObject *key) 
    PyObject* PyDict_GetItemString( PyObject *p, const char *key) 
      使用Python/C API中的PyDict_GetItem()函数和PyDict_GetItemString()函数来获取字典中某项的值。它们都返回项的值。
    其参数含义如下。p:要进行操作的字典,key:添加项的关键字,
    对于PyDict_GetItem()函数其为PyObject型
    对于PyDict_GetItemString()函数其为char型。 
    
    PyObject* PyDict_Items( PyObject *p) 
    PyObject* PyDict_Keys( PyObject *p) 
    PyObject* PyDict_Values( PyObject *p) 
      在Python/C API中提供了与Python中字典操作相对应的函数。例如
      字典的item方法对应于PyDict_Items()函数。
      字典的keys方法对应于PyDict_Keys()函数。
      字典的values方法对应于PyDict_Values()函数。
    其参数p:要进行操作的字典。
    
  • 返回值解析
    python执行完返回的结果也是PyObject类型,因此需要将PyObject类型转换为C/C++类型。

int PyArg_Parse( PyObject *args, char *format, ...)
    根据format把args的值转换成c类型的值,[format](https://docs.python.org/3/c-api/arg.html)接受的类型和上述Py_BuildValue()的是一样的,
  • 释放资源
    Python使用引用计数机制对内存进行管理,实现自动垃圾回收。在Python/C API中提供了Py_CLEAR()、Py_DECREF()等宏来对引用计数进行操作。
    当使用Python/C API中的函数创建列表、元组、字典等后,就在内存中生成了这些对象的引用计数,在对其完成操作后应该使用Py_CLEAR()、Py_DECREF()等宏来销毁这些对象。
void Py_CLEAR(PyObject *o)
void Py_DECREF(PyObject *o)
其中,o的含义是要进行操作的对象。
对于Py_CLEAR()其参数可以为NULL指针,此时,Py_CLEAR()不进行任何操作。而对于Py_DECREF()其参数不能为NULL指针,否则将导致错误。

二、调用简单语句

首先编写CMakeLists.txt文件,引入python所需头文件和库文件,python版本是3.6.5

  • CMakeLists.txt
cmake_minimum_required(VERSION 3.9)

project(helloworld)

set(SDK_VERSION 0_0_1)
# >>> build type 
set(CMAKE_BUILD_TYPE "Release")				# 指定生成的版本
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb")
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
# <<<


# >>> CXX11 
set(CMAKE_CXX_STANDARD 11)				# C++ 11 编译器
SET(CMAKE_CXX_STANDARD_REQUIRED TRUE)
# <<< 


# >>> Python3 
set(PYTHON_ROOT "/home/zjh/anaconda3/envs/learn")
message("python root: " ${PYTHON_ROOT})
include_directories(${PYTHON_ROOT}/include/)
link_directories(${PYTHON_ROOT}/lib/)
# <<<

# --- generate ---
add_executable(helloworld helloworld.cpp)
target_link_libraries(helloworld -lpython3.6m)
  • helloworld.cpp
#include <python3.6m/Python.h>

int main(int argc, char *argv[])
{
    wchar_t *program = Py_DecodeLocale(argv[0], nullptr);
    if ( program == nullptr ){
        std::cout << "Fatal Error: cannot decode argv[0]!" << std::endl;
        return -1;
    }
    Py_SetProgramName(program);
    Py_Initialize();    ## 初始化

    PyRun_SimpleString("print('hello world!')");

    Py_Finalize();      ## 释放资源
    PyMem_RawFree(program);

    return 0;
}

CMakeLists.txthelloworld.cpp两个文件放在同一文件夹下,在新建build文件夹,进行编译构建,构建成功后目录下会生成helloworld可执行文件。

$ cmake..
$ make

参考链接:

https://docs.python.org/2/extending/embedding.html
https://zhuanlan.zhihu.com/p/79896193
https://blog.csdn.net/ziweipolaris/article/details/83689597
https://blog.csdn.net/u011681952/article/details/92765549
https://blog.csdn.net/hnlylyb/article/details/89498651

posted @ 2021-03-02 21:37  半夜打老虎  阅读(4891)  评论(1编辑  收藏  举报