什么是用SWIG封装对象从C ++调用Python函数的最干净方法是什么 [英] What Is The Cleanest Way to Call A Python Function From C++ with a SWIG Wrapped Object

查看:124
本文介绍了什么是用SWIG封装对象从C ++调用Python函数的最干净方法是什么的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码,该代码通过Python回调函数实现了一个简单的C ++类(ObjWithPyCallback).想法是使用"this"作为单个参数来调用Python函数.

I have the following code, which implements a simple C++ class (ObjWithPyCallback) with a Python callback function. The idea is to call the Python function with "this" as the single argument.

问题在于,由于ObjWithPyCallback是SWIG包装的对象,因此我需要SWIG typeinfo才能创建Python对象.

The problem is that since ObjWithPyCallback is a SWIG wrapped object I need the SWIG typeinfo in order to create a Python object.

此问题是它在SWIG生成的文件"ObjWithPyCallback_wrap.cxx"中.SWIG可以生成头文件吗?到目前为止,我还无法实现这一目标.

The problem with this is that it's inside of the SWIG generated file "ObjWithPyCallback_wrap.cxx". Can SWIG generate a header file? I have thus far not been able to make this happen.

但是,即使有了头文件,SWIG和我的主要实现之间仍然存在循环依赖关系,这很烦人.我想找到一种避免的方法.最终,ObjWithPyCallback最终位于与Python绑定不同的共享库中.

However, even with a header file there is a circular dependency between SWIG and my main implementation, which is annoying. I'd like to find a way to avoid it if at all possible. Ultimately ObjWithPyCallback ends up in a different shared library than the Python bindings.

是否有一种干净的方法可以实现这一目标?我知道这篇文章,但它仅解决了SWIG_NewPointerObj的机制

Is there a clean way to pull this off? I'm aware of this post, but it only addresses the mechanics of SWIG_NewPointerObj.

在此先感谢您的帮助!

代码如下:

文件:example.py

File: example.py

import cb

def foo(x=None):
    print("Hello from Foo!")
    # I'd like x to be a reference to a ObjWithPyCallback object.
    print(x)

o = cb.ObjWithPyCallback()
o.setCallback(foo)
o.call()

文件:ObjWithPyCallback.h

File: ObjWithPyCallback.h

#include <Python.h>

class ObjWithPyCallback 
{
   public:

      ObjWithPyCallback();
      void setCallback(PyObject *callback);
      void call();

      PyObject *callback_;
};

文件:ObjWithCallback.cpp

File: ObjWithCallback.cpp

#include "ObjWithPyCallback.h"

#include <iostream>

ObjWithPyCallback::ObjWithPyCallback() : callback_(NULL) {}

void ObjWithPyCallback::setCallback(PyObject* callback)
{
   if (!PyCallable_Check(callback))
   {
      std::cerr << "Object is not callable.\n";
   }
   else
   {
      if ( callback_ ) Py_XDECREF(callback_);
      callback_ = callback;
      Py_XINCREF(callback_);
   }
}

void ObjWithPyCallback::call()
{
   if ( ! callback_ )
   {
      std::cerr << "No callback is set.\n";
   }
   else
   {
      // I want to call "callback_(*this)", how to do this cleanly?
      PyObject *result = PyObject_CallFunction(callback_, "");
      if (result == NULL)
         std::cerr << "Callback call failed.\n";
      else
         Py_DECREF(result);
   }
}

文件:: ObjWithPyCallback.i

File:: ObjWithPyCallback.i

%module cb
%{
   #include "ObjWithPyCallback.h"
%}

%include "ObjWithPyCallback.h"

推荐答案

下面是我用于解决此问题的可行解决方案.它使用了@omnifarious和@flexo的建议.

Below is my working solution for solving this problem. It uses the suggestions from both @omnifarious and @flexo above.

特别是,我们使用SWIG导向器创建了一个Callback类,然后在Python中从其派生以获取所需的回调功能,而不会引入循环依赖.

In particular we create a Callback class with a SWIG director and then derive from it in Python to get the required callback functionality without introducing a circular dependency.

此外,我们提供了一个接口,该接口允许任何可调用的Python对象充当回调.我们通过在SWIG中使用"pythonprend"指令为"setCallback"函数添加一些代码来实现此目的.此代码仅检查可调用对象,如果找到可调用对象,则将其包装在Callback实例中.

In addition we provide an interface which allows any Python object that is callable to act as a callback. We achieve this by using the "pythonprend" directive in SWIG to prepend some code to the "setCallback" function. This code simply checks for a callable object and if it finds one, wraps it in an instance of a Callback.

最后,我们处理与使C ++类(ObjWithPyCallback)引用导演对象(即Callback的子类)有关的内存问题.

Finally we deal with the memory issues related to having a C++ class (ObjWithPyCallback) reference a director object (i.e. a subclass of Callback).

文件example.py:

File example.py:

import cb

class CB(cb.Callback):
    def __init__(self):
        super(CB, self).__init__()
    def call(self, x):
        print("Hello from CB!")
        print(x)

def foo(x):
    print("Hello from foo!")
    print(x)

class Bar:
    def __call__(self, x):
        print("Hello from Bar!")
        print(x)


o = cb.ObjWithPyCallback()
mycb=CB()
o.setCallback(mycb)
o.call()
o.setCallback(foo)
o.call()
o.setCallback(Bar())
o.call()

文件ObjWithPyCallback.i:

File ObjWithPyCallback.i:

%module(directors="1") cb
%{
   #include "Callback.h"
   #include "ObjWithPyCallback.h"
%}
%feature("director") Callback;
%feature("nodirector") ObjWithPyCallback;

%feature("pythonprepend") ObjWithPyCallback::setCallback(Callback&) %{
   if len(args) == 1 and (not isinstance(args[0], Callback) and callable(args[0])):
      class CallableWrapper(Callback):
         def __init__(self, f):
            super(CallableWrapper, self).__init__()
            self.f_ = f
         def call(self, obj):
            self.f_(obj)

      args = tuple([CallableWrapper(args[0])])
      args[0].__disown__()
   elif len(args) == 1 and isinstance(args[0], Callback):
      args[0].__disown__()


%}

%include "Callback.h"
%include "ObjWithPyCallback.h"

文件回调.h:

#ifndef CALLBACK_H
#define CALLBACK_H

class ObjWithPyCallback;

class Callback
{
   public:
      Callback(){}

      virtual ~Callback(){}
      virtual void call(ObjWithPyCallback& object){} 
};

#endif

文件ObjWithPyCallback.h:

File ObjWithPyCallback.h:

#ifndef OBJWITHPYCALLBACK_H
#define OBJWITHPYCALLBACK_H

class Callback;

class ObjWithPyCallback 
{
   public:

      ObjWithPyCallback();
      ~ObjWithPyCallback();
      void setCallback(Callback &callback);
      void call();

   private:

      Callback* callback_;
};

#endif

文件ObjWithPyCallback.cpp:

File ObjWithPyCallback.cpp:

#include "ObjWithPyCallback.h"
#include "Callback.h"

#include <iostream>

ObjWithPyCallback::ObjWithPyCallback() : callback_(NULL) {}

ObjWithPyCallback::~ObjWithPyCallback()
{
}

void ObjWithPyCallback::setCallback(Callback &callback)
{
   callback_ = &callback;
}

void ObjWithPyCallback::call()
{
   if ( ! callback_ )
   {
      std::cerr << "No callback is set.\n";
   }
   else
   {
      callback_->call(*this);
   }
}

这篇关于什么是用SWIG封装对象从C ++调用Python函数的最干净方法是什么的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆