【问题标题】:Passing a C++ object to Python将 C++ 对象传递给 Python
【发布时间】:2018-11-09 19:12:39
【问题描述】:

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

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

%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()

这个问题是关于如何让 python execute() 函数在从 c++ 调用时以 C++ 对象作为参数工作。

编写了以下 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;
}

运行上述代码时,execute() python 函数以 MyClass 对象作为参数,失败并返回 NULL。但是,输入了Python函数,在控制台输出中我可以看到输出(Entering execute function),表明传递的对象不是,确实,一个有效的MyClass 对象。

有很多关于如何将简单类型(如整数、双精度或字符串类型)从 C/C++ 传递给 Python 的示例。但是很少有例子展示如何传递一个 C/C++ 对象/指针,这有点令人费解。

上面的代码,带有一个 CMake 文件,可以从 github 上签出: https://github.com/TotteKarlsson/miniprojects/tree/master/passMeToPython

此代码不使用任何 boost python 或其他 API。不过,Cython 听起来很有趣,如果它可以用于 C++ 方面的简化,它是可以接受的。

【问题讨论】:

  • 你有没有想过用 JSON 或 xml 序列化它,然后使用构造函数在 python 中构建对象?
  • 不。我的目标是能够共享对象的内存,因此不需要复制。
  • 您的 sn-ps 中没有 pFunc 对象。它不是那样工作的。 myObject 必须是一个有效 PyObject(我不认为它是 - 在我看到MyObject 的定义之前我无法确定)。 “C++ 类已经用 swig 封装”:那么我认为你不应该被要求编写这个 C 代码。
  • 感谢 CristiFati,在这个问题中,python 解释器嵌入在 C++ 应用程序中,因此上面的 python 函数是从 C/C++ 调用的。 MyObject 是一个空的“演示”C++ 类,它的内部是无关紧要的。它仅用于正确理解概念,正如标题中所述,概念是“如何将 C++ 对象传递给 Python?”

标签: python c++ swig


【解决方案1】:

这是对我自己问题的部分回答。我说的是片面的,因为我确实相信有更好的方法。

以这篇文章为基础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++代码中包含生成的header,允许编写如下代码:

    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 之间传递和检索数据,但在我看来它并不理想。

swig 头文件在 C++ 代码中的使用确实不是很漂亮,此外,它需要用户“手动”查看 swig 生成的包装器代码才能找到“_p_MyClass”代码。

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

【讨论】:

  • 这就是答案。如果你愿意,你应该可以写SWIG_TypeQuery("MyClass *")
  • 我可能想做的是通过包装的 C++ 函数而不是作为参数来创建 execute 'find' obj 实例,例如它调用api.getInstance(),其中api 是通过PyImport_AppendInittab() 提供的。
  • 感谢 Flexo,也许包含 swig 包装标题还不错。并且能够使用您的建议查询类型,使用“MyClass *”消除了必须查看 swig 包装器代码内部的必要性,这是一个很大的改进,imo。不确定如何使用 AppendInittab 技术。
【解决方案2】:

不幸的是,它在我的情况下不起作用。 我在 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 编译器

【讨论】:

  • 如果您想导出指向 MyClass 类的指针,请尝试SWIG_TypeQuery("MyClass *")
【解决方案3】:
PyObject *pValue;
pValue = PyObject_CallMethod(pInstance, "add","(i)",x);
if (pValue)
    Py_DECREF(pValue);
else
    PyErr_Print();

【讨论】:

  • 好奇,什么是 pInstance、x 和“add”?
猜你喜欢
  • 1970-01-01
  • 2012-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多