如何通过 Python/C API 将 Python 实例传递给 C++

Posted

技术标签:

【中文标题】如何通过 Python/C API 将 Python 实例传递给 C++【英文标题】:How to pass Python instance to C++ via Python/C API 【发布时间】:2015-04-28 19:20:10 【问题描述】:

我通过使用 SWIG 2.0 包装接口来使用 Python (2.7) 扩展我的库,并有一个我想在其中创建访问者的图形对象。在 C++ 中,界面如下所示:

    struct Visitor
    
        virtual void OnStateBegin() = 0;
        virtual void OnNode(Node* n) = 0;
        virtual void OnStateEnd() = 0;
    ;

我想在 Python 中定义一个等效的类,全部在 python 中定义,允许定义访问者:

class GraphVisitor:
    def __init__(self, label):
        self._label = label
        print("__init__(self,0)".format(self._label))
    def OnStateBegin(self):
        print("OnStateBegin()" + self._label)
    def OnNode(self, i_node):
        print("OnNode()" + self._label)
    def OnStateEnd(self):
        print("OnStateEnd()" + self._label)

我要做的是在 python 脚本中创建一个 GraphVisitor 的实例,并从 C++ 中为给定的实例调用 OnStateBegin()、OnNode() 和 OnStateEnd() 方法。这是我想在 Python 中做的事情:

#model is a SWIG wrapped class
mvis = GraphVisitor("This is a test")
model.Visit("mvis") # I'm not sure how to pass the instance 'mvis' to C++?

在我由 Swig 包装的 C++ 中,我不确定如何获取实例“mvis”?我可以调用 Python 中定义的函数没问题,但实例让我很难过!

【问题讨论】:

mvis 只是 python 类的一个实例。它与您的struct Visitor 有关。在 C/C++ 中,您只能以 PyObject* 的形式访问它。 我知道。我试图描述我正在尝试在 python 中定义一个访问者,而这就是 c++ 对应项。 你看过我之前的回答了吗:***.com/questions/9040669/…(你可以跳过关于嵌入的部分,但是关于从 PyObject 转换为 C++ 接口的部分正是你想要的) 柔印 - 非常感谢!你的东西真的很有帮助! 【参考方案1】:

为了解决这个问题,我从模块中检索了给定模块名和类名的类(下面的代码假设模块尚未加载):

void Model::Visit(const char* mod_name, const char* class_name)

    PyErr_Clear();
    PyObject* mod_name_obj = PyString_FromString(mod_name);
    PyObject* class_name_obj = PyString_FromString(class_name);

    PyObject* py_module = PyImport_Import(mod_name_obj);
    PyObject* err_1 = PyErr_Occurred();
    if(err_1)
        PyErr_Print();

一旦我有了这个模块,我就从它的字典中查找了这个类:

    if(py_module)
    
        PyObject* py_module_dict = PyModule_GetDict(py_module);
        PyObject* py_class = PyDict_GetItem(py_module_dict, class_name_obj);

我通过在 C++ 中实例化 python 类稍微简化了我的问题,然后创建了我的访问者,最后访问了它:

        if(py_class && PyClass_Check(py_class) && PyCallable_Check(py_class))
        
            PyObject* inst = PyInstance_New(py_class, 0, 0);

            if(inst && PyInstance_Check(inst))
            
                IModel::IVisitorPtr py_visitor = new PyModelVisitor(inst);

                _model->Visit(py_visitor);
            
        
    

访问者有 3 个函数 OnStateBegin()、OnNode() 和 OnStateEnd()。我在我的 SWIG python 绑定生成器中添加了一个选项来生成一个头文件,用于使用-external-runtime option 对 SWIG 运行时进行外部访问,因此我可以在 C++(下面的 INode*)中创建一个类并将其作为参数传递给 Python python OnNode() 成员函数如下(为简洁起见,删除了错误检查):

VisitorCtrl OnNode(INode* node)

    Node* node_impl = new NodeImpl(node);
    PyObject* pynode = SWIG_NewPointerObj(node_impl, SWIG_TypeQuery("Node *"), 0);
    PyObject* result = PyObject_CallMethodObjArgs(_inst, PyString_FromString("OnNode"), pynode, 0);

    long rivis = PyInt_AsLong(result);

    return(static_cast<VisitorCtrl>(rivis));

【讨论】:

【参考方案2】:

我不知道 SWIG 是否可以做到这一点,但您可以通过 SIP 做到这一点。

sip_vector_test.h:

class EXPORT Node 
public:
    explicit Node(int n) : n_(n) ;
    int getN() const  return n_; 
private:
    int n_;
;
struct EXPORT NodeVisitor 
    virtual void OnNode(Node* n) = 0;
;
struct EXPORT Graph 
public:
    void addNode(int num);
    void accept(NodeVisitor *nv);
private:
    std::vector< std::shared_ptr<Node> > nodes_;
;

visitor.sip:

%Module pyvisit

%ModuleHeaderCode
#include "sip_visitor_test.h"
%End

class Node 
public:
    explicit Node(int n);
    int getN() const;
;
struct NodeVisitor 
    virtual void OnNode(Node* n) = 0;
;
struct Graph 
public:
    void addNode(int num);
    void accept(NodeVisitor *nv);
;

在 Python 中使用它:

>>> import pyvisit
>>> g = pyvisit.Graph()
>>> g.addNode(3)
>>> g.addNode(5)
>>> class PyNodeVisitor(pyvisit.NodeVisitor):
>>>     def OnNode(self, node):
>>>         print(node.getN())
>>> pnv = PyNodeVisitor()
>>> g.accept(pnv)
3
5

我已将包含此测试项目源代码的zip file 放在我的主页上。

【讨论】:

非常感谢您的回答!我没有使用 SIP,想看看是否可以在不添加新类型的情况下完成。

以上是关于如何通过 Python/C API 将 Python 实例传递给 C++的主要内容,如果未能解决你的问题,请参考以下文章

如何将 websocket 推送 api 输出写入文本文件?

Python / C-Api:向模块添加类

Python如何将RGB图像转换为Pytho灰度图像?

Python C API 引用计数器

使用 Python C API 将 Python 整数列表传递给 C 函数

Python C Api 将 PyObject * 传输到 c 数组中