我正在编写一个旨在在不同平台上运行的应用程序。 我的基础库是用C ++编写的,我想使用SWIG生成特定于平台的代码(Java / Android,C#/ Windows,Objective C / iOS)。
这里我使用的是我希望从Java传递的Message-Objects - > C ++和C ++ - > Java的。这个想法是,有一个基本类型“CoreMessage”和派生消息“StatusMessage”,“DebugMessage”......
对我而言,通过这样做来保持界面通用似乎很自然:
C ++:
class CoreMessage {
}
class StatusMessage : public CoreMessage {
public:
string getStatusMessage();
}
class DebugMessage : public CoreMessage {
public:
... some stuff
}
C ++中的(双向)接口代码应如下所示:
CoreMessage waitForNextMessage(); // Java calls this and blocks until new message is available in a message queue
void processMessage(CoreMessage m); // Java calls this and puts a new message in an eventhandling mechanism
我的Swig文件非常基础,它只包含类定义和界面 - 没有别的。
现在,Swig为Java生成所有类和接口,包括Java中消息的继承:
public class CoreMessage {
...
}
public class StatusMessage extends CoreMessage {
...
}
public class DebugMessage extends CoreMessage {
...
}
主Java-Module现在看起来像这样:
public native void processMessage(CoreMessage);
public native CoreMessage waitForNextMessage();
当我尝试从Java调用此代码时:
StatusMessage m = new StatusMessage();
processMessage(m);
这个C ++代码将被执行并导致错误:
void processMessage(CoreMessage in) {
StatusMessage* statusPtr = dynamic_case<StatusMessage*>(&in); // works
StatusMessage status = *(statusPtr); // This will cause a runtime error
}
另一个方向的相同问题:C ++代码
TsCoreMessage waitForMessage() {
StatusMessage m = StatusMessage();
return m;
}
使用此Java代码,通过JNI和SWIG生成的包装器调用C ++:
CoreMessage msg = waitForMessage();
// msg instanceof StatusMessage returns false
StatusMessage s = (StatusMessage) msg; // Causes java.lang.ClassCaseException
所以对我而言,当将对象从一种语言传递给另一种语言时,似乎JNI会丢失类型信息......
我确实阅读了一些主题和文章,但我没有找到解决方案 - 我需要一个双向解决方案。
我不确定“导演”不是我要找的东西? 据我了解导演,他们是为类而编写的,手工扩展java并允许UP-cast到相应的C ++ BaseClass ......但我不确定我对导演的理解; - )
答案 0 :(得分:0)
关键是:我必须使用指针(在我的例子中是boost :: shared:ptr)。
所以我的消息(messages.h)现在看起来像这样:
typedef boost::shared_ptr<CoreMessage> CoreMessagePtr;
public class CoreMessage {
...
}
typedef boost::shared_ptr<StatusMessage> StatusMessagePtr;
public class StatusMessage extends CoreMessage {
...
}
typedef boost::shared_ptr<DebugMessage> DebugMessagePtr;
public class DebugMessage extends CoreMessage {
...
}
并且还需要将这些功能更改为这些指针:
CoreMessagePtr waitForNextMessage();
void processMessage(CoreMessagePtr m);
然后,我不得不使用boost智能指针的附带模板告诉Swig有关shared_ptr的信息:
%module myapi
%include <boost_shared_ptr.i> // Tells Swig what to do with boost::shared_ptr
%header %{ // Tells Swig to add includes to the header of the wrapper
#include <boost/shared_ptr.hpp>
#include "messages.h"
%}
// Now here is the magic: Tell Swig that we want to use smart-Pointers for
// these classes. It makes Swig generate all necessary functions and wrap
// away all the de-referencing.
%shared_ptr(CoreMessage)
%shared_ptr(StatusMessage)
%shared_ptr(DebugMessage)
%include "message.h" // Finally: Tell Swig which interfaces it should create
嗯,我们现在得到的:Swig生成的Java类与我们以前的类非常相似。但是有一件小事可以产生不同:构造函数现在看起来像这样:
protected StatusMessage(long cPtr, boolean cMemoryOwn) {
super(tscoreapiJNI.StatusMessage_SWIGSmartPtrUpcast(cPtr), true);
swigCMemOwnDerived = cMemoryOwn;
swigCPtr = cPtr;
}
并且魔术线是超级的(tscoreapiJNI.StatusMessage_SWIGSmartPtrUpcast(cPtr),true); 该行包装指针的动态转换,它允许从Java转换对象。
但是:它并不像它可能那么容易(也许我只是找不到解决方案?!)
您无法使用Java强制转换来完成[Java Code]的工作:
CoreMessage m = waitForMessage(); // Returns a StatusMessage
StatusMessage mm = (StatusMessage) m; // Throws ClassCaseException
这是因为java无法进行必要的动态强制转换但需要C ++代码。我的解决方案看起来像这样并使用函数模板:
我将此模板添加到messages.h
template <class T>
static boost::shared_ptr<T> cast(TsCoreMessagePtr coreMsg) {
return boost::dynamic_pointer_cast<T>(coreMsg);
}
现在告诉Swig如何处理这个模板[添加到interface.i的末尾]:
%template(castStatusMessage) cast<StatusMessage>;
%template(castDebugMessage) cast<DebugMessage>;
会发生什么:Swig将这两个函数添加到myapi.java:
public static StatusMessage castStatusMessage(CoreMessage coreMsg) {
long cPtr = myapiJNI.castStatusMessage(CoreMessage.getCPtr(coreMsg), coreMsg);
return (cPtr == 0) ? null : new StatusMessage(cPtr, true);
}
public static DebugMessage castDebugMessage(CoreMessage coreMsg) {
long cPtr = myapiJNI.castDebugMessage(CoreMessage.getCPtr(coreMsg), coreMsg);
return (cPtr == 0) ? null : new DebugMessage(cPtr, true);
}
最后你可以在java代码中使用它:
CoreMessage m = waitForMessage(); // Returns a StatusMessage
StatusMessage mm = myapi.cast(m);
答案 1 :(得分:0)
请注意,SWIG文档有一个官方解决方案:
http://www.swig.org/Doc1.3/Java.html#adding_downcasts
简而言之,您可以通过以下方式为派生类型添加静态方法:
%exception Ambulance::dynamic_cast(Vehicle *vehicle) {
$action
if (!result) {
jclass excep = jenv->FindClass("java/lang/ClassCastException");
if (excep) {
jenv->ThrowNew(excep, "dynamic_cast exception");
}
}
}
%extend Ambulance {
static Ambulance *dynamic_cast(Vehicle *vehicle) {
return dynamic_cast<Ambulance *>(vehicle);
}
};
并在java中使用它:
Ambulance ambulance = Ambulance.dynamic_cast(vehicle);
ambulance.sound_siren();
希望它有所帮助。