我正在将一个c ++应用程序移植到Java。我使用SWIG生成了一些Java类和JNI包装器。
在c ++中,我有一个名为Lion的类,它扩展了Animal并实现了Killable。我收到一条警告,说Java中不存在多重继承。到目前为止没问题,Lion只会在我的Java类中扩展Animal。
使用SWIG生成的类:
public class Killable {
private long swigCPtr;
protected boolean swigCMemOwn;
public Killable(long cPtr, boolean cMemoryOwn) {
swigCMemOwn = cMemoryOwn;
swigCPtr = cPtr;
}
protected static long getCPtr(Killable obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected void finalize() {
delete();
}
public synchronized void delete() {
if (swigCPtr != 0) {
if (swigCMemOwn) {
swigCMemOwn = false;
cppinterfaceJNI.delete_Killable(swigCPtr);
}
swigCPtr = 0;
}
}
public long getKillableId() {
return cppinterfaceJNI.Killable_getId(swigCPtr, this);
}
public void kill() {
cppinterfaceJNI.Killable_kill(swigCPtr, this);
}
}
public class Lion extends Animal {
private long swigCPtr;
public Lion(long cPtr, boolean cMemoryOwn) {
super(cppinterfaceJNI.Lion_SWIGUpcast(cPtr), cMemoryOwn);
swigCPtr = cPtr;
}
public static long getCPtr(Lion obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
...
}
现在我们说我想要一个Killable阵列。 由于使用SWIG生成的Java类存储了指向原始c ++类的指针,因此我应该可以执行以下操作:
LinkedList<Killable> list = new LinkedList<Killable>();
Killable k = new Killable(Lion.getCPtr(myLionObject), false);
list.add(k);
System.out.println(k.getKillableId()) // Return a long, no crash here but I got a huge number (wrong number)
k.kill(); // Crash
当我这样做时没有错误但是当我尝试访问我的对象的方法时k库崩溃了。我认为发生崩溃是因为没有找到正确的实现,但我不明白为什么,因为我给了一个有效的指针指向新对象。知道崩溃发生的原因或我可以在这里使用的解决方法吗?
感谢您的帮助
答案 0 :(得分:2)
正如您已经看到SWIG+Java doesn't result in pure virtual classes becoming interfaces automatically的组合。
问题在于,像你所展示的那样直接用getCPtr()
操纵对象类似于写下这样的东西:
Lion *l = new lion;
intptr_t ptr = (intptr_t)l;
Killable *k = (Killable*)ptr;
在C ++中 - 使用C样式转换是不好的形式,因为它们掩盖了未定义的行为。它极不可能按预期工作,即使它确实会比崩溃更糟,因为你的代码库中有未确定的未定义行为。幸运的是,SWIG有一个简单的解决方法,你想要进行这样的演员(通常只是一个自动转换)。
假设我们有以下头文件:
#include <iostream>
class Killable {
public:
virtual ~Killable() {}
virtual void die() = 0;
};
class Animal {
public:
virtual ~Animal() {}
virtual void moo() { std::cout << "The dog says: meow\n"; }
};
class Lion : public Animal, public Killable {
virtual void die() { std::cout << "Deaded\n"; }
};
我们可以使用以下SWIG接口成功包装:
%module test
%{
#include "test.h"
%}
%include "test.h"
%extend Animal {
Killable *toKillable() {
return dynamic_cast<Killable*>($self);
}
}
这里,%extend
添加了另一个成员函数,用于处理不会自动暴露的强制转换。如果转换是正确的,则返回有效的Killable
。如果不是,那么将返回null
。
我们可以在Java中使用它:
import java.util.LinkedList;
public class run {
public static void main(String[] argv) {
System.loadLibrary("test");
LinkedList<Killable> list = new LinkedList<Killable>();
Lion l = new Lion();
l.moo();
Killable k = l.toKillable();
list.add(k);
k.die();
}
}