Python与c++的相互调用(一)

部署运行你感兴趣的模型镜像

    最近公司项目使用python,个人突然对游戏引擎中python与c++之间的交互产生了兴趣。正赶上朋友要我帮忙做个五行相生相克的演示程序,于是想到写个hge的python导出库,顺便熟悉下python的c api。特写下此文,记录python与C++相互调用的要点,以便日后查阅。

准备工作

        首先是配置开发环境了,可以选择编译python源代码,或者直接使用python的安装包。如果想要发布出去,在无python环境的用户机上直接运行,建议直接编译源代码。

下面是我们熟悉的过程,在VS中配置好python的路径、代码中include python的头文件、以及将lib引入工程。

如果你不是编译的源代码,在debug下,链接时会找不到pythonxx_d.lib。简单的解决办法是:

python的安装目录下 include/pyconfig.h368行(我使用的是python2.7)。将定义Py_DEBUG的宏注释掉:

/*

#ifdef _DEBUG

#define Py_DEBUG

#endif

*/

           再到326行将python27_d.lib改为python27.lib

ifdef _DEBUG

#pragma comment(lib,"python27.lib")

#elif

#pragma comment(lib,"python27.lib")

#endif /* _DEBUG */

这样就可以通过链接。

 

1. python调用C++函数

一. Python解释器的开启与关闭

        本文是这个系列第一篇、主要是讲述如何将C++中定义的函数导出到python中调用。

        首先在程序中先启动python的解释器:

                    Py_Initialize();

        这个函数要在调用其他任何python API函数之前调用。

        对应的,在程序结束的时候,需要关闭:

                    Py_Finalize();        

 

二. C++函数的导出

        下面说明如何导出C++的函数到py本文的最后会提到如何将函数注册到module中,以及如何在Py中调用C++导出的函数。

static PyObject*  Function(PyObject *pSelf, PyObject *pParams)

{

.......

}

        所有函数都以这种形式导出。由于不是某类型的成员函数,可以不理会第一个参数pSelf。第二个参数pParams是从py中传进来的参数。这里解释个概念,PyObject是一个结构体,它代表了Python相关的数据。实际上,在PythonAPI中任何东西都可用PyObject结构体来表示,如:整型、字符串、函数或者整个脚本。PythonC++之间数据解析的关键就是搞清楚PyObject,即:如何将PyObject解析成C++类型,传给C++函数(传参)。如何将C++类型解析成PyObject,用以返回(传给python的返回值)

       给个简单的例子:

 static PyObject* Print(PyObject *pSelf, PyObject *pParams)

{

       printf("hello my python world");

       Py_INCREF(Py_None);

       return Py_None;

}

        这就是个最简单、无参数的函数。

        下面个例子可以了解如何解析从py传来的参数,以及返回值。

static PyObject* Add(PyObject *pSelf, PyObject *pParams)

{

float x, y;

if (!PyArg_ParseTuple(pParams, "ff", &x, &y))

{

return NULL;

}

float result = x + y;

return PyFloat_FromDouble(result);

}

        解释下这个函数PyArg_ParseTuple。Py的参数传递,其实是以tuple的形式进行的。pParams实际上是个Python  Tuple Object的指针,这个tuple的每个元素对应于py函数的参数PyArg_ParseTuple函数可以将一个Tuple类型,用代表格式的字符串解析成C++类型。见上例,其中pParams就是从PY传进来的参数,"ff"是代表格式的字符串,f代表浮点型。x, y是返回值。好了,解析一个py传进来的参数就是这么简单。可能有人会问,我怎么知道他传几个参数、以及参数的类型?别忘了,这个函数是你提供给py脚本程序员的,参数个数是你自己定的。

    返回值PyFloat_FromDouble是用double创建了一个指向PyFloat类型的指针,内容是函数计算的结果。这里也可以使用另一个函数Py_BuildValue("f", result);第一个参数也是代表格式的字符串。

    下面罗列一下,各常用字符串所代表的类型。

s 
s# 
z 
z# 
b 
h 
i 
l(小写的L) 
c 
f 
d 
D 
O 
O! 
O& 
S 
(items) 
| 
: 
; 

三. 将函数注册到python的module中,导出module到python

      文档中将要导出到python的函数称作 host api。

      首先,我们需要将要导出函数的名字、函数地址等信息罗列到PyMethodDef类型的数组中,如:

static PyMethodDef Methods[]=

 {

      {"print", Print,METH_NOARGS,    "print hello to python"},

      {"add", Add,METH_VARARGS,    "add two float."},

      {NULL,NULL,0,NULL} 
 };

PyMethodDef成员分别是:导出到python的函数名,函数指针, 参数形式(常用的就这两种:METH_NOARGS代表没有参数。METH_VARARGS:代表有参数),函数的doc,在py中可以通过__doc__打印出来。最后一行的NULL是结束标记。

     下面要注册module到python了。    

if  (!PyImport_AddModule("Sample"))
{
     cout<<"Host API module could not be created."<<endl;
}

PyObject* module = Py_InitModule("Sample", Methods);
if (!module)
{
     cout<<"Host Api functions could not be initialized."<<endl;
     return;
}

     函数PyImport_AddModule创建了一个module, Py_InitModule在init这个module的时候,将定义host api的数组传进去。这样就可以了

四. python中调用这些函数

import Sample

Sample.print()

result = Sample.add(1, 2)

print result

五. 具体例子

      往往简单的例子不能让人快速的将技术使用到项目中,下面给出,我做HGE导出的一段函数。

C++:

static PyObject* HGE_System_Effect_Load(PyObject *pSelf, PyObject *pParams)
{
     char* effectPath;
     if (!PyArg_ParseTuple(pParams, "s", &effectPath))
     {
         return NULL;
     }
     DWORD effectHandle = g_hge->Effect_Load(effectPath);
     return PyInt_FromLong(effectHandle);
}

.......

static PyMethodDef HostAPIFuncs[] =
{

.......
{"Effect", HGE_System_Effect_Load, METH_VARARGS, NULL},

.......
{NULL, NULL, NULL, NULL}

};

 

........

if  (!PyImport_AddModule("Hge"))
 {
         cout<<"Host API module could not be created."<<endl;
 }

 PyObject* module = Py_InitModule("Hge", HostAPIFuncs);

.........

Python:

import Hge

eff = Hge.Effect(".....")

这个函数实际上就是将hge的 Effect_Load函数导出到py中调用。

 

 下一篇,将介绍如何导出C++定义的类型、以及成员函数和属性

 

 

 

   


 

您可能感兴趣的与本文相关的镜像

Python3.10

Python3.10

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

PythonC++相互调用的方法如下: ### Python调用C++代码 #### 1. 使用ctypes ctypes是Python个外部函数库,它提供了C语言兼容的数据类型,并允许调用动态链接库中的函数。对于简单的C代码可以使用这种方式调用。例如对于下面的C代码: ```c #include <stdio.h> #include <stdlib.h> int foo(int a, int b) { printf("you input %d and %d\n", a, b); return a + b; } ``` 可以将其编译成动态链接库(如在Linux下生成`.so`文件,在Windows下生成`.dll`文件),然后在Python中使用ctypes调用: ```python import ctypes # 加载动态链接库 lib = ctypes.CDLL('./your_lib.so') # 替换为实际的库文件名 # 指定函数参数和返回值类型 lib.foo.argtypes = [ctypes.c_int, ctypes.c_int] lib.foo.restype = ctypes.c_int # 调用函数 result = lib.foo(1, 2) print(result) ``` #### 2. 使用pybind11 pybind11是个轻量级的头文件库,它可以在C++代码和Python代码之间创建无缝接口。首先定义C++函数: ```cpp #include <pybind11/pybind11.h> int add(int a, int b) { return a + b; } namespace py = pybind11; PYBIND11_MODULE(example, m) { m.doc() = "pybind11 example plugin"; // 模块文档字符串 m.def("add", &add, "A function which adds two numbers"); // 绑定函数 } ``` 然后使用`setuptools`编译这个C++代码为Python模块,在Python中导入并调用: ```python import example result = example.add(1, 2) print(result) ``` ### C++调用Python代码 #### 使用Python的原生C API Python提供了个C API,允许在C/C++程序中嵌入Python代码。首先需要确保Python开发包已经安装,可以通过`#include <Python.h>`来使用Python C API。示例代码如下: ```cpp #include <Python.h> int main() { // 初始化Python解释器 Py_Initialize(); // 运行简单的Python代码 PyRun_SimpleString("print('Hello from Python in C++!')"); // 关闭Python解释器 Py_Finalize(); return 0; } ``` 编译时需要链接Python库,例如在Linux下使用`g++ your_file.cpp -o your_program -I /usr/include/python3.x -lpython3.x`(替换为实际的Python版本)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值