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++类和Java之间遇到的一个“常见”问题。该问题称为downcasting,是directors的常见问题。不幸的是,即使是这种简单的情况,导演似乎也无法处理。我已经尝试了导演的所有组合-如下所示
%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";
}
};
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);
}
}