3

可能重复:
SWIG Java Retaining Class information of the objects bouncing from C++

问题:为什么我的 C++ swigged 对象在传递给 Java 回调函数时会丢失其类型?

设置:我采用 Swig Java 示例进行回调,并添加了一个要传递给回调的对象run(Parent p)。回调按预期工作,但是当我传递一个Child对象时,Java 似乎失去了它的类型并认为Parent它应该是它的类型Child。这是基于Swig java 回调示例

系统信息:带有 Swig 1.3.33 的 Ubuntu 8.04 - 万一最新的 Swig 有所作为,我也测试了 1.3.39 - 没有效果。

输出

bash$ java -Djava.library.path=。运行我
添加和调用正常的 C++ 回调
--------------------------------------
回调::run(5Child)
回调::~Callback()

添加和调用 Java 回调
----------------------------------
JavaCallback.run(父)
回调::run(5Child)
回调::~Callback()

正如您在输出中看到的那样 - 对象实际上是 Child 类型 - 但它的 Java 类名称是 Parent - 这是错误的......

如果您查看 Java 回调run(Parent p),您可以看到我在哪里获取 Java 类,并且 Java 确实认为这个对象是类型的Parent- 尝试将其强制转换为 Child 将按ClassCastException预期抛出。

代码

/* File : example.i */
%module(directors="1") example
%{
#include "example.h"
%}

%include "std_string.i"

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

%include "example.h"




/* File : example.h */
#include <string>
#include <cstdio>
#include <iostream>
#include <typeinfo>

class Parent {
public:
    virtual const char* getName() {
        return typeid(*this).name();
    }
};


class Child : virtual public Parent {
};



class Callback {
public:
    virtual ~Callback() { std::cout << "Callback::~Callback()" << std:: endl; }
    virtual void run(Parent& p) { std::cout << "Callback::run(" << p.getName() << ")" << std::endl; }
};


class Caller {
private:
    Callback *_callback;
public:
    Caller(): _callback(0) {}
    ~Caller() { delCallback(); }
    void delCallback() { delete _callback; _callback = 0; }
    void setCallback(Callback *cb) { delCallback(); _callback = cb; }
    void call() {
        Parent *p = new Child();
        if (_callback) 
            _callback->run(*p);
        delete p;
    }
};



/* File: runme.java */
public class runme
{
  static {
    try {
        System.loadLibrary("example");
    } catch (UnsatisfiedLinkError e) {
      System.err.println("Native code library failed to load. See the chapter on Dynamic Linking Problems in the SWIG Java documentation for help.\n" + e);
      System.exit(1);
    }
  }

  public static void main(String[] args)
  {
    System.out.println("Adding and calling a normal C++ callback");
    System.out.println("----------------------------------------");

    Caller              caller = new Caller();
    Callback            callback = new Callback();

    caller.setCallback(callback);
    caller.call();
    caller.delCallback();

    callback = new JavaCallback();

    System.out.println();
    System.out.println("Adding and calling a Java callback");
    System.out.println("------------------------------------");

    caller.setCallback(callback);
    caller.call();
    caller.delCallback();

    // Test that a double delete does not occur as the object has already been deleted from the C++ layer.
    // Note that the garbage collector can also call the delete() method via the finalizer (callback.finalize())
    // at any point after here.
    callback.delete();

    System.out.println();
    System.out.println("java exit");
  }
}

class JavaCallback extends Callback
{
  public JavaCallback()
  {
    super();
  }

  public void run(Parent p)
  {
    System.out.println("JavaCallback.run("+p.getClass().getSimpleName()+")");
    super.run(p);
  }
}




# File: Makefile
TOP        = ../..
SWIG       = $(TOP)/../preinst-swig
CXXSRCS    = example.cxx
TARGET     = example
INTERFACE  = example.i
SWIGOPT    =

all::   java

java::
    $(MAKE) -f $(TOP)/Makefile CXXSRCS='$(CXXSRCS)' SWIG='$(SWIG)' \
    SWIGOPT='$(SWIGOPT)' TARGET='$(TARGET)' INTERFACE='$(INTERFACE)' java_cpp
    javac *.java

clean::
    $(MAKE) -f $(TOP)/Makefile java_clean

check: all

这可能是 Swig 中的一个错误 - 但我希望这是我对 C++ 类型/转换的愚蠢......

任何想法将不胜感激!

4

1 回答 1

1

在周末研究了这个问题之后,我猜这是 Swig 在 C++ 类和 Java 之间存在的“常见”问题。这个问题被称为垂头丧气,是董事们的普遍问题。不幸的是,即使是这个简单的案例,董事们似乎也无法处理。我已经尝试过导演的每一种组合 - 如下所示

%feature("director") Callback;
%feature("director") Parent;
%feature("director") Child;

这些似乎都没有帮助,但做以下黑客工作正常:

class Callback {
public:
    virtual ~Callback() { std::cout << "Callback::~Callback()" << std:: endl; }
    virtual void run(Parent& p) {
        std::cout << "Callback::run1(" << p.getName() << ")\n";
    }

    virtual void run(Child& c) {
        std::cout << "Callback::run2(" << c.getName() << ")\n";
    }
};

然后在你需要的任何子类型的 java 类中,重载会自行解决。

class JavaCallback extends Callback
{
  public void run(Child p)
  {
    out.p("JavaCallback.run("+p.getClass().getSimpleName()+")");
    out.p("p.getName() = "+p.getName());
    super.run(p);
  }
}

然后神奇地输出工作

bash$ java -Djava.library.path=。运行我
添加和调用正常的 C++ 回调
--------------------------------------
做孩子
子类型类父
回调::run2(5Child)
回调::~Callback()
添加和调用 Java 回调
----------------------------------
JavaCallback.run(子)
p.getName() = 5Child
回调::run2(5Child)
回调::~Callback()
退出

可能应该有更好的方法来做到这一点,但 Swig 文档都没有向我展示如何正确执行此操作的清晰示例。在 libsbml 库中有一些非常令人印象深刻的代码可以帮助人们创建向下转换的类型映射来解决问题,但事实证明这对于很少的输出来说非常复杂......无论如何,这很简单。

如果有人能想出一个简单的(人类)解决方案,我很想听听它。

我今天看到一篇博文,它专门讨论了 SWIG、C++、C# 中的向下转换类型——无论如何,这可能是一个不错的方向。

于 2009-07-05T21:49:08.550 回答