Question:Why is my C++ swigged object losing its type when passed to a Java callback function?

Setup:I've taken the Swig Java example for doing callbacks and added an object to be passed to the callback run(Parent p) . The callback works as expected but when I pass a Child object the Java seems to lose its type and think its of type Parent when it should be Child . This is based on the Swig java callback example

系统信息:
带有Swig 1.3.33的Ubuntu 8.04-在偶然的机会下,最新的Swig有所作为,我也测试了1.3.39-无效。

输出:

bash$ java -Djava.library.path=. runme
Adding and calling a normal C++ callback
----------------------------------------
Callback::run(5Child)
Callback::~Callback()

Adding and calling a Java callback
------------------------------------
JavaCallback.run(Parent)
Callback::run(5Child)
Callback::~Callback()

As you can see in the outputs - the object is really of type Child - but its Java class name is Parent - which is wrong...

If you look in the Java callback run(Parent p) you can see where I'm fetching the Java class, and Java really does think this object is of type Parent - trying to cast this to Child will throw ClassCastException as expected.

Code:

/* 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++类型/广播的愚蠢...

任何想法将不胜感激!

最佳答案

在周末解决了这个问题之后,我猜这是Swig在C++类和Java之间遇到的一个“常见”问题。该问题称为downcasting,是directors的常见问题。不幸的是,即使是这种简单的情况,导演似乎也无法处理。我已经尝试了导演的所有组合-如下所示

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

似乎没有任何帮助,但执行以下hack可以正常工作:
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++回调
----------------------------------------
做 child
子类型类 parent
回调:: run2(5Child)
回调::〜Callback()
添加并调用Java回调
------------------------------------
JavaCallback.run(Child)
p.getName()= 5 child
回调:: run2(5Child)
回调::〜Callback()
Java导出

可能应该有一个更好的方法来执行此操作,但是Swig文档中没有一个向我提供了如何正确执行此操作的清晰示例。 libsbml库中有一些非常令人印象深刻的代码,可以帮助人们创建向下转换的类型映射来解决该问题,但是事实证明,这种方法非常复杂,几乎没有输出……总之这很容易。

如果有人能找到一个简单的(人为的)解决方案,那么我将对它感兴趣。

我今天遇到了一个博客帖子,它是specifically is talking about downcasting types in SWIG, C++, C#-无论如何,这可能是一个很好的方向。

07-26 00:00
查看更多