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.
System Info: Ubuntu 8.04 w/ Swig 1.3.33 - on the off chance the latest Swig made a difference I also tested 1.3.39 - which had no effect.
Outputs:
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
This might be a bug in Swig - but I'm hoping that this is my being stupid with C++ types/casting...
Any thoughts would be greatly appreciated!