这个问题是关于如何将C++对象传递给在(C++)嵌入式Python解释器中调用的python函数.
以下C++类(MyClass.h)用于测试:
#ifndef MyClassH
#define MyClassH
#include <string>
using std::string;
class MyClass
{
public:
MyClass(const string& lbl): label(lbl) {}
~MyClass(){}
string getLabel() {return label;}
private:
string label;
};
#endif
Run Code Online (Sandbox Code Playgroud)
暴露C++类的python模块可以通过以下Swig接口文件生成:
%module passmetopython
%{ #include "MyClass.h" %}
%include "std_string.i"
//Expose to Python
%include "MyClass.h"
Run Code Online (Sandbox Code Playgroud)
下面是使用python模块的Python脚本
import passmetopython as pmtp
def execute(obj):
#This function is to be called from C/C++, with a
#MyClass object as an argument
print ("Entering execute function")
lbl = obj.getLabel();
print ("Printing from within python execute function. Object label is: " + lbl)
return True
def main():
c = pmtp.MyClass("Test 1")
retValue = execute(c)
print("Return value: " + str(retValue))
#Test function from within python
if __name__ == '__main__':
main()
Run Code Online (Sandbox Code Playgroud)
这个问题是关于如何使用C++对象作为参数从c ++调用时使python execute()函数正常工作.
编写以下C++程序来测试函数(最小错误检查量):
#include "Python.h"
#include <iostream>
#include <sstream>
#include "MyClass.h"
using namespace std;
int main()
{
MyClass obj("In C++");
cout << "Object label: \"" << obj.getLabel() << "\"" << endl;
//Setup the Python interpreter and eventually call the execute function in the
//demo python script
Py_Initialize();
//Load python Demo script, "passmetopythonDemo.py"
string PyModule("passmetopythonDemo");
PyObject* pm = PyUnicode_DecodeFSDefault(PyModule.c_str());
PyRun_SimpleString("import sys");
stringstream cmd;
cmd << "sys.path.append(\"" << "." << "\")";
PyRun_SimpleString(cmd.str().c_str());
PyObject* PyModuleP = PyImport_Import(pm);
Py_DECREF(pm);
//Now create PyObjects for the Python functions that we want to call
PyObject* pFunc = PyObject_GetAttrString(PyModuleP, "execute");
if(pFunc)
{
//Setup argument
PyObject* pArgs = PyTuple_New(1);
//Construct a PyObject* from long
PyObject* pObj(NULL);
/* My current attempt to create avalid argument to Python */
pObj = PyLong_FromLong((long) &obj);
PyTuple_SetItem(pArgs, 0, pObj);
/***** Calling python here *****/
cout<<endl<<"Calling function with an MyClass argument\n\n";
PyObject* res = PyObject_CallObject(pFunc, pArgs);
if(!res)
{
cerr << "Failed calling function..";
}
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
运行上面的代码时,带有MyClass对象作为参数的execute() python函数失败并返回NULL.但是,输入了Python函数,因为我可以在控制台输出中看到输出(输入执行函数),表明传递的对象实际上不是有效的MyClass对象.
关于如何将简单类型(如整数,双精度或字符串类型)从C/C++传递到Python,有很多例子.但是很少有例子展示如何传递C/C++对象/指针,这有点令人费解.
上面的代码,带有CMake文件,可以从github中查看:https: //github.com/TotteKarlsson/miniprojects/tree/master/passMeToPython
此代码不使用任何boost python或其他API.Cython听起来很有趣,如果它可以用来简化C++方面,那么它是可以接受的.
这是我自己问题的部分答案.我说偏爱,因为我相信有更好的方法.
在这篇文章的基础上http://swig.10945.n7.nabble.com/Pass-a-Swig-wrapped-C-class-to-embedded-Python-code-td8812.html 我生成了swig运行时标头,如上所述这里,第15.4节:http://www.swig.org/Doc2.0/Modules.html#Modules_external_run_time
在上面的C++代码中包含生成的头,允许编写以下代码:
PyObject* pObj = SWIG_NewPointerObj((void*)&obj, SWIG_TypeQuery("_p_MyClass"), 0 );
Run Code Online (Sandbox Code Playgroud)
此代码使用来自Swig python包装源文件的信息,即MyClass类型的"swig"名称,即_p_MyClass.
使用上面的PyObject*作为PyObject_CallObject函数的参数,上面代码中的python execute()函数执行正常,Python代码使用生成的python模块,可以正确访问MyClass对象的内部数据.这很棒.
虽然上面的代码说明了如何以非常简单的方式传递和检索C++和Python之间的数据,但在我看来,它并不理想.
在C++代码中使用swig头文件实际上并不那么漂亮,此外,它还需要用户"手动"查看swig生成的包装器代码,以便找到"_p_MyClass"代码.
肯定有更好的办法!?也许应该在swig接口文件中添加一些内容以使其看起来更好看?