从C++;使用SWIG包装的对象 我有下面的代码,它实现了一个简单的C++类(ObjutpyPrCuBube),带有Python回调函数。其思想是使用“this”作为单个参数调用Python函数

从C++;使用SWIG包装的对象 我有下面的代码,它实现了一个简单的C++类(ObjutpyPrCuBube),带有Python回调函数。其思想是使用“this”作为单个参数调用Python函数,c++,python,swig,C++,Python,Swig,问题在于,由于ObjWithPyCallback是一个SWIG包装的对象,所以我需要SWIG类型信息来创建Python对象 问题是它在SWIG生成的文件“ObjWithPyCallback_wrap.cxx”中。SWIG可以生成头文件吗?到目前为止,我还未能做到这一点 然而,即使使用头文件,SWIG和我的主实现之间也存在循环依赖关系,这很烦人。如果可能的话,我想找到一个避免的方法。最终,ObjWithPyCallback会出现在与Python绑定不同的共享库中 有没有一个干净的方法来解决这个问题

问题在于,由于ObjWithPyCallback是一个SWIG包装的对象,所以我需要SWIG类型信息来创建Python对象

问题是它在SWIG生成的文件“ObjWithPyCallback_wrap.cxx”中。SWIG可以生成头文件吗?到目前为止,我还未能做到这一点

然而,即使使用头文件,SWIG和我的主实现之间也存在循环依赖关系,这很烦人。如果可能的话,我想找到一个避免的方法。最终,ObjWithPyCallback会出现在与Python绑定不同的共享库中

有没有一个干净的方法来解决这个问题?我知道,但它只涉及SWIG_NewPointerObj的机制

提前感谢您的帮助

代码如下:

文件: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

#include <Python.h>

class ObjWithPyCallback 
{
   public:

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

      PyObject *callback_;
};

我将使用SWIG的机制来处理继承,并拥有一个带有虚拟函数的回调类
void call()
。然后使用SWIG使该类能够从Python中派生


在Python中,您只需确定设置回调的位置,就将其包在由C++回调类派生的Python类的实例中,并使其“代码>调用< /COD>成员函数执行回调。这也是你要做的测试,看看它是否可以调用。然后用这个包装器对象调用
setCallback
函数。

下面是我解决这个问题的有效解决方案。它使用了上述@omnifarious和@flexo的建议

特别是,我们使用SWIG控制器创建一个回调类,然后在Python中从中派生,以获得所需的回调功能,而不引入循环依赖项

此外,我们还提供了一个接口,允许任何可调用的Python对象充当回调。我们通过使用SWIG中的“pythonprend”指令为“setCallback”函数预先编写一些代码来实现这一点。这段代码只是检查一个可调用对象,如果它找到了一个,则将其包装到回调实例中

最后,我们处理与C++类(ObjutPyCalBuff])引用有关的内存问题,即引用对象(即回调的子类)。 文件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:

%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
#ifndef OBJWITHPYCALLBACK_H
#define OBJWITHPYCALLBACK_H

class Callback;

class ObjWithPyCallback 
{
   public:

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

   private:

      Callback* callback_;
};

#endif
文件ObjWithPyCallback.h:

#ifndef CALLBACK_H
#define CALLBACK_H

class ObjWithPyCallback;

class Callback
{
   public:
      Callback(){}

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

#endif
#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:

#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);
   }
}
#包括“ObjWithPyCallback.h”
#包括“Callback.h”
#包括
ObjWithPyCallback::ObjWithPyCallback():回调函数(NULL){}
ObjWithPyCallback::~ObjWithPyCallback()
{
}
void ObjWithPyCallback::setCallback(回调和回调)
{
回调\=&callback;
}
void ObjWithPyCallback::call()
{
如果(!回调函数)
{
标准::cerr呼叫(*此);
}
}
1。解决问题的总体思路: (1). 定义一个名为回调的C++类,它具有一个方法Run()。 (2). 继承Python代码中的回调,并创建实例。 (3). 使用C++方法将实例绑定到C++点子。 (4). 使用指针访问run(),它是在python代码中定义的。 2.示例代码 (1). 例h (2). example.cxx 3.编撰 4.在pythonshell中使用 5.其他 我想你还需要更多的东西。尝试:

$ ls swig-x.x.x/Examples/python

我已经继承了这个(用Python创建的派生类中的Python版本重写C++虚拟函数),但是它提供了一个直的回调函数。C++我需要一个回调类的导演,对吧?这似乎能很好地工作。我会试一试,然后再打给你。谢谢@克雷格维特:我已经很久没有做过了(大概9年左右),以至于我有点忘了我到底做了什么。我只知道这是可能的,因为我记得曾经做过一次。:-)@CraigWright-第一部分介绍了这一点。不过,你可以放心地忽略其余的部分。我的同事使用我们的“真实代码”成功地遵循了你的建议。我将完成上面的示例并发布它。再次感谢。@CraigWright:不客气,不过柔印也应该得到很多赞扬。Flexo指出的答案是关于使用director功能的一个很好的教程。ObjWithPyCallback中有一个错误。i,您的“elif lens(args)”应该是“elif len(args)”。否则就行了。我不明白什么是args。这是swig python生成器生成的数据吗?Swig版本3.0.10没有生成这样的变量。@Jonathan:请注意,如果在Swig中发生了更改,请确定。对我有效的方法是在接口文件中使用以下内容:%feature(“shadow”)ObjWithPyCallback::setCallback(&)%{def setCallback(self,*args):#原始答案$action(self,args[0])%%中的代码很可能已经更改。这是大约5年前的。我也不再使用这种技术了。如果你有更好/更现代的方法,发布它,我会更新答案。
#include <iostream>
#include "example.h"

int n=0;
Callback * callback = NULL;

void Callback::run(int n){ 
    std::cout << "This print from C++: n = " << n << std::endl;
}    

void setCallback(Callback * cb){
    callback = cb; 
}    

void doSomeWithCallback(){
    if(callback == NULL){
        std::cout << "Must set callback first!" << std::endl;
    }else{
        callback->run(n++);
    }                                                                                                                                                                                         
}
/* File : example.i */                                                                                                                                                                        
%module(directors="1") example
%{
#include "example.h"                                                                                                                                                                          
%}

/* turn on director wrapping Callback */
%feature("director") Callback;

%include "example.h"
$ swig -c++ -python example.i
$ g++ -c -fPIC example.cxx example_wrap.cxx -I/usr/include/python2.7/
$ g++ -shared example.o example_wrap.o -o _example.so
In [1]: import example

In [2]: example.doSomeWithCallback()
Must set callback first!

In [3]: callback = example.Callback()

In [4]: example.setCallback(callback)

In [5]: example.doSomeWithCallback()
This print from C++: n = 0

In [6]: class Callback(example.Callback):
   ...:     def run(self, n):
   ...:         print 'This print from Python: n =', n
   ...:         

In [7]: callback =  Callback()

In [8]: example.setCallback(callback)

In [9]: example.doSomeWithCallback()
This print from Python: n = 1
$ ls swig-x.x.x/Examples/python