可能重复:
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 callback example。
系统信息: Ubuntu 8.04 w / Swig 1.3.33 - 关于最新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()
正如你在输出中看到的那样 - 对象实际上是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 ++类型/转换的愚蠢......
任何想法都将不胜感激!
答案 0 :(得分:1)
在周末解决这个问题后,我猜这是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";
}
};
然后在java类中,对于任何子类型,你需要重载irons本身。
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=. runme Adding and calling a normal C++ callback ---------------------------------------- make child child type class Parent Callback::run2(5Child) Callback::~Callback() Adding and calling a Java callback ------------------------------------ JavaCallback.run(Child) p.getName() = 5Child Callback::run2(5Child) Callback::~Callback() java exit
应该有更好的方法来做到这一点,但没有一个Swig文档向我提供了如何正确执行此操作的明确示例。在libsbml库中有一些非常令人印象深刻的代码可能会帮助人们创建解决问题的向下转换类型图,但这对于输出很少来说非常复杂......无论如何这很简单。
如果有人能够找到一个简单的(人类)解决方案,我会有兴趣听到它。
我今天遇到了一篇博客文章specifically is talking about downcasting types in SWIG, C++, C# - 无论如何,这可能是一个很好的方向。