5

显然,SWIG 不理解std::function并破坏了 Python 绑定。例如,这在 C++ 中有效:

// Somewhere in the API
typedef std::function<void(const UnitError & error)> UnitErrorHandler;

// Somewhere else in the API
void Unit::setErrorHandler(const UnitErrorHandler & handler) {}

// In the application code
unit->setErrorHandler([](const UnitError & error){
    std::cerr << error << std::endl;
    std::exit(1);
});

但这会破坏代码(除了为了简单起见有不同的行为,但这不是重点):

unit.setErrorHandler(lambda error: len(error))

情况与def(普通)未绑定函数相同。那么,有人知道解决方法吗?

4

2 回答 2

1

std::function相对较新(C++11),因此 SWIG 没有任何开箱即用的解决方案。虽然没有那么灵活,但您可以使用函数指针,但需要注意的是。在文档中引用5.4.9 指向函数和回调的指针:

...SWIG 提供对函数指针的完全支持,前提是回调函数是用 C 而不是目标语言定义的。

所以传递一个 lambda 是行不通的。阅读文档链接以获取一些替代方案。

于 2013-06-26T03:38:28.200 回答
1

分两行:创建一个接口函数,它将命令作为字符串并设置一个仿函数,然后将其分配给std::function. operator()负责评估目标语言中的字符串。


让我们假设在头文件mymodule.hh中有一个类,其中MyModule的方法是另一个对象。void MyModule::Func(const std::function<double(Bunch&)> & func)Bunch

这可以通过定义一个接口函数来解决,该函数接受一个const char *表示表达式的 eval 并将其包装到一个仿函数中,然后将其分配给std::function. 好消息是,这完全可以在 swig 接口文件中完成,无需接触 C++ 代码,方法如下(我使用了 tcl,你只需要适应operator()):

%module mymodule
%{
  #include "bunch.hh"
  #include "mymodule.hh"

  extern Tcl_Interp* tcl_interp;

  struct Tcl_bunch_callback {
    std::string callback;
    double operator()(Bunch & bunch)
    {
      Tcl_Obj * bunch_obj = SWIG_Tcl_NewInstanceObj(tcl_interp, &bunch, SWIGTYPE_p_Bunch, /*own pointer?*/0);
      Tcl_SetVar2Ex(tcl_interp, "bunch", (const char*)nullptr, bunch_obj, 0);
      double resultValue;
      const int resultCode = Tcl_ExprDouble(tcl_interp, callback.c_str(), &resultValue);
      if (resultCode != TCL_OK) {
        std::cerr << "WARNING evaluation of tcl expression failed: " 
                  << Tcl_GetStringResult(tcl_interp) << std::endl;
        resultValue = max_double;
      }
      Tcl_DeleteCommand(tcl_interp, Tcl_GetString(bunch_obj)); //remove the used command to avoid leaks
      return resultValue;
    }
  };
%}

%include "bunch.hh"
%include "mymodule.hh"
%extend MyModule
{
  void Func(const char * cmd) {
    $self->Func(std::function<double(Bunch&)>(Tcl_bunch_callback(cmd)));
  }
}

在我的情况下,operator()它非常受 Tcl 限制,但我确信类似的程序也可以为其他目标语言编写。跟随它的一些细节。

我让用户能够直接从 Tcl 访问当前Bunch在 C++ 中处理的方法。该函数:SWIG_NewInstanceObj允许将Bunch指针转换为目标语言的表示形式并在解释器中创建它的一个实例(此函数没有记录,但在任何 swig 生成的包装文件中挖掘一点并不难理解它机制)。使用以下命令,我将该对象设置为一个名为的变量bunch,以便用户只需使用它就可以使用它,$bunch然后可以访问使用 swig 导出的所有方法。

多亏了 swig,我认为用这么少的代码就可以实现如此强大的功能真是太神奇了!

于 2014-08-07T07:45:53.000 回答