简体   繁体   English

将 C++ 对象传递给 Python

[英]Passing a C++ object to Python

This question is about how to pass a C++ object to a python function that is called in a (C++) embedded Python interpreter .这个问题是关于如何将 C++ 对象传递给在 (C++) 嵌入式 Python 解释器中调用的 python 函数

The following C++ class (MyClass.h) is designed for testing:以下 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

A python module, exposing the C++ class, can be generated by the following Swig interface file:可以通过以下 Swig 接口文件生成一个暴露 C++ 类的 python 模块:

%module passmetopython

%{    #include "MyClass.h"    %}

%include "std_string.i"

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

Below is a Python script using the python module下面是一个使用 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()

This question is about how to get the python execute() function working, when called from c++, with a C++ object as an argument.这个问题是关于如何让 python execute()函数在从 C++ 调用时以 C++ 对象作为参数工作。

The following C++ program was written to test the functions (minimum amount of error checking):编写了以下 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;
}

When running the above code, the execute() python function, with a MyClass object as an argument, fails and returns NULL.运行上述代码时,以 MyClass 对象作为参数的execute() python 函数失败并返回 NULL。 However, the Python function is entered, as I can see the output ( Entering execute function ) in the console output, indicating that the object passed is not, indeed , a valid MyClass object.但是,进入了Python函数,因为我可以看到控制台输出中的输出( Entering execute function ),表明传递的对象确实不是有效的MyClass对象。

There are a lot of examples on how to pass simple types, like ints, doubles or string types to Python from C/C++.有很多关于如何从 C/C++ 向 Python 传递简单类型(如整数、双精度或字符串类型)的示例。 But there are very few example showing how to pass a C/C++ object/ pointer, which is kind of puzzling.但是很少有例子展示如何传递C/C++对象/指针,这有点令人费解。

The above code, with a CMake file, can be checked out from github: https://github.com/TotteKarlsson/miniprojects/tree/master/passMeToPython上面的代码,带有一个CMake文件,可以从github上检出: https : //github.com/TotteKarlsson/miniprojects/tree/master/passMeToPython

This code is not to use any boost python or other API's.此代码不使用任何 boost python 或其他 API。 Cython sounds interesting though, and if it can be used to simplify on the C++ side, it could be acceptable. Cython 听起来很有趣,如果它可以用于简化 C++ 方面,它可能是可以接受的。

This is a partial answer to my own question.这是对我自己的问题的部分回答。 I'm saying partial, because I do believe there is a better way.我说部分,因为我相信有更好的方法。

Building on this post http://swig.10945.n7.nabble.com/Pass-a-Swig-wrapped-C-class-to-embedded-Python-code-td8812.html I generated the swig runtime header, as described here, section 15.4: http://www.swig.org/Doc2.0/Modules.html#Modules_external_run_time基于这篇文章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

Including the generated header in the C++ code above, allow the following code to be written:在上面的 C++ 代码中包含生成的头文件,允许编写以下代码:

    PyObject* pObj = SWIG_NewPointerObj((void*)&obj, SWIG_TypeQuery("_p_MyClass"),  0 ); 

This code is using information from the Swig python wrap source files, namely the "swig" name of the type MyClass, ie _p_MyClass .此代码使用来自 Swig python 包装源文件的信息,即 MyClass 类型的“swig”名称,即_p_MyClass

With the above PyObject* as an argument to the PyObject_CallObject function, the python execute() function in the code above executes fine, and the Python code, using the generated python module, do have proper access to the MyClass objects internal data.使用上面的 PyObject* 作为 PyObject_CallObject 函数的参数,上面代码中的 python execute()函数执行得很好,并且使用生成的 python 模块的 Python 代码确实可以正确访问 MyClass 对象的内部数据。 This is great.这很棒。

Although the above code illustrate how to pass, and retrieve data between C++ and Python in a quite simple fashion, its not ideal, in my opinion.虽然上面的代码以一种非常简单的方式说明了如何在 C++ 和 Python 之间传递和检索数据,但在我看来它并不理想。

The usage of the swig header file in the C++ code is really not that pretty, and in addition, it requires a user to "manually" look into swig generated wrapper code in order to find the "_p_MyClass" code. C++ 代码中 swig 头文件的使用确实不是那么漂亮,此外,它需要用户“手动”查看 swig 生成的包装代码以找到“_p_MyClass”代码。

There must be a better way!?一定会有更好的办法!? Perhaps something should be added to the swig interface file in order to get this looking nicer?也许应该在 swig 接口文件中添加一些东西以使它看起来更好?

Unfortunatle it does not work in my case.不幸的是,它在我的情况下不起作用。 I get nullptr exception inside SWIG_TypeQuery("_p_MyClass") macros.我在SWIG_TypeQuery("_p_MyClass")宏中得到 nullptr 异常。

Could you please share more details how to do such passing?您能否分享更多详细信息如何进行此类传递?

  1. I have MyClass library我有 MyClass 库
  2. Based on this library I created module using swig基于这个库,我使用 swig 创建了模块
  3. Next I created swigpyrun.h file using -external-runtime接下来我使用 -external-runtime 创建了 swigpyrun.h 文件
  4. I included this file into my main.cpp, and SWIG_TypeQuery("_p_MyClass") gives an error我将此文件包含在我的 main.cpp 中,并且 SWIG_TypeQuery("_p_MyClass") 给出了一个错误

I even tried to add definitions like SWIG_TYPE_TABLE, but it did not help.我什至尝试添加像 SWIG_TYPE_TABLE 这样的定义,但没有帮助。

BTW I am using MSVS compilter顺便说一句,我正在使用 MSVS 编译器

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM