将C ++对象传递给Python

时间:2018-05-30 20:38:20

标签: python c++ swig

这个问题是关于如何将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

暴露C ++类的python模块可以通过以下Swig接口文件生成:

%module passmetopython

%{    #include "MyClass.h"    %}

%include "std_string.i"

//Expose to Python
%include "MyClass.h"

下面是使用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()

这个问题是关于如何使用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;
}

运行上面的代码时,带有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 ++方面,那就可以接受了。

3 个答案:

答案 0 :(得分:8)

这是我自己问题的部分答案。我说偏爱,因为我相信有更好的方法。

在此帖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 ); 

此代码使用Swig python包装源文件中的信息,即MyClass类型的“swig”名称,即 _p_MyClass

使用上面的PyObject *作为PyObject_CallObject函数的参数,上面代码中的python execute()函数执行正常,使用生成的python模块的Python代码确实有访问MyClass对象的内部数据。这很棒。

虽然上面的代码说明了如何以非常简单的方式传递和检索C ++和Python之间的数据,但在我看来它并不理想。

在C ++代码中使用swig头文件实际上并不那么漂亮,此外,它还要求用户“手动”查看swig生成的包装器代码,以便找到“_p_MyClass”代码。

一定有更好的方法!?也许应该在swig接口文件中添加一些内容以使其看起来更好看?

答案 1 :(得分:0)

不幸的是,它在我的情况下不起作用。 我在 SWIG_TypeQuery("_p_MyClass") 宏中遇到 nullptr 异常。

能否请您分享更多有关如何进行此类传球的详细信息?

  1. 我有 MyClass 库
  2. 基于这个库,我使用 swig 创建了模块
  3. 接下来我使用 -external-runtime 创建了 swigpyrun.h 文件
  4. 我将此文件包含在 main.cpp 中,但 SWIG_TypeQuery("_p_MyClass") 出现错误

我什至尝试添加 SWIG_TYPE_TABLE 之类的定义,但没有帮助。

顺便说一句,我正在使用 MSVS 编译器

答案 2 :(得分:-1)

PyObject *pValue;
pValue = PyObject_CallMethod(pInstance, "add","(i)",x);
if (pValue)
    Py_DECREF(pValue);
else
    PyErr_Print();