使用 SWIG 生成 Java 接口
我正在使用 SWIG 制作 C++ 库的 Java 包装器(关于 Json(反)序列化)以在 Android 上使用它。我在 C++ 中定义了一个抽象类,表示一个可以序列化(反序列化)的对象:
class IJsonSerializable {
public:
virtual void serialize(Value &root) = 0;
virtual void deserialize(Value &root) = 0;
};
现在,我尝试从该类生成一个 Java 接口。这是我的 SWIG 接口:
%module JsonSerializable
%{
#include "JsonSerializable.hpp"
%}
%import "JsonValue.i"
class IJsonSerializable {
public:
virtual void serialize(Value &root) = 0;
virtual void deserialize(Value &root) = 0;
};
但是生成的 Java 代码(显然,因为我无法找到如何告诉 SWIG 这是一个接口)一个简单的类,具有两个方法和一个默认构造函数/析构函数:
public class IJsonSerializable {
private long swigCPtr;
protected boolean swigCMemOwn;
public IJsonSerializable(long cPtr, boolean cMemoryOwn) {
swigCMemOwn = cMemoryOwn;
swigCPtr = cPtr;
}
public static long getCPtr(IJsonSerializable obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected void finalize() {
delete();
}
public synchronized void delete() {
if (swigCPtr != 0) {
if (swigCMemOwn) {
swigCMemOwn = false;
JsonSerializableJNI.delete_IJsonSerializable(swigCPtr);
}
swigCPtr = 0;
}
}
public void serialize(Value root) {
JsonSerializableJNI.IJsonSerializable_serialize(swigCPtr, this, Value.getCPtr(root), root);
}
public void deserialize(Value root) {
JsonSerializableJNI.IJsonSerializable_deserialize(swigCPtr, this, Value.getCPtr(root), root);
}
}
如何生成一个与 SWIG 的有效接口?
I'm using SWIG to make a Java wrapper of a C++ library (about Json (de)serialization) to use it on Android. I defined an abstract class in C++, representing an object which can be (de)serialized :
class IJsonSerializable {
public:
virtual void serialize(Value &root) = 0;
virtual void deserialize(Value &root) = 0;
};
Now, I'm trying to generate from this class a Java interface. Here's my SWIG interface:
%module JsonSerializable
%{
#include "JsonSerializable.hpp"
%}
%import "JsonValue.i"
class IJsonSerializable {
public:
virtual void serialize(Value &root) = 0;
virtual void deserialize(Value &root) = 0;
};
But the generated Java code is (obviously, as I was not able to find out how to tell SWIG that's an interface) a simple class, with the two methods and a default constructor/destructor:
public class IJsonSerializable {
private long swigCPtr;
protected boolean swigCMemOwn;
public IJsonSerializable(long cPtr, boolean cMemoryOwn) {
swigCMemOwn = cMemoryOwn;
swigCPtr = cPtr;
}
public static long getCPtr(IJsonSerializable obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected void finalize() {
delete();
}
public synchronized void delete() {
if (swigCPtr != 0) {
if (swigCMemOwn) {
swigCMemOwn = false;
JsonSerializableJNI.delete_IJsonSerializable(swigCPtr);
}
swigCPtr = 0;
}
}
public void serialize(Value root) {
JsonSerializableJNI.IJsonSerializable_serialize(swigCPtr, this, Value.getCPtr(root), root);
}
public void deserialize(Value root) {
JsonSerializableJNI.IJsonSerializable_deserialize(swigCPtr, this, Value.getCPtr(root), root);
}
}
How can I generate a valid interface with SWIG ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您可以使用“Directors”,但是它并不像您希望的那样直接从 C++ 抽象类映射到 Java。因此,我的答案分为三个部分 - 首先是在 Java 中实现 C++ 纯虚函数的简单示例,其次是对输出为何如此的解释,第三是“解决方法”。
在 Java 中实现 C++ 接口
给定一个头文件 (
module.hh
):我们希望对其进行包装并使其从 Java 端直观地工作。我们可以通过定义以下 SWIG 接口来做到这一点:
这里我们为整个模块启用了控制器,然后请求它们专门用于
类接口
。除了这个和我最喜欢的“自动加载共享对象”代码之外,没有什么特别值得注意的。我们可以使用以下 Java 类对此进行测试:然后我们可以运行它并查看它是否按预期工作:
如果您对它既不是
抽象
也不是界面
感到满意,您可以停止阅读这里,导演会做您需要的一切。为什么 SWIG 生成
类
而不是接口
?然而,SWIG 将看似抽象的类变成了具体的类。这意味着在 Java 方面我们可以合法地编写
new Interface();
,这是没有意义的。 SWIG 为什么这样做?类
甚至不是抽象
,更不用说接口
了(参见第4点此处),这在 Java 方面感觉更自然。答案是双重的:界面
中完成。考虑我们包装以下函数的情况:
这里,SWIG 除了了解其
Interface
类型之外,对返回类型一无所知。在理想的情况下,它会知道派生类型是什么,但仅从函数签名来看,它无法弄清楚这一点。这意味着在生成的 Java某处中,必须调用new Interface
,如果Interface
则这是不可能/合法的> 在 Java 方面是抽象的。可能的解决方法
如果您希望将其作为接口提供,以便在 Java 中表达具有多重继承的类型层次结构,那么这将是相当有限的。不过,有一个解决方法:
手动将接口编写为正确的 Java 接口:
修改 SWIG 接口文件:
Interface
重命名为NativeInterface
。 (我们也应该让它只对有问题的包可见,我们包装的代码位于它自己的包中,以避免人们做“疯狂”的事情。Interface
的地方,SWIG 现在将使用NativeInterface
作为 Java 端的类型。我们需要类型映射将函数参数中的NativeInterface
映射到我们手动添加的Interface
Java 接口。NativeInterface
标记为实现Interface
,以使 Java 端行为对 Java 用户来说自然且可信。Interface
的代理,而无需成为NativeInterface
。NativeInterface
,但并非所有Interface
都是一个(尽管所有NativeInterface
都会) ,因此我们提供了一些粘合来使Interface
表现得像NativeInterfaces
,以及一个类型映射来应用该粘合。 (有关pgcppname本文档代码>)
这会产生一个现在看起来像这样的模块文件:
现在我们可以包装一个函数,如:
并使用它,如下所示:
现在,它按您希望的方式运行:
我们已经将 C++ 的抽象类包装为 Java 中的接口,正如 Java 程序员所期望的那样!
You can achieve what you're looking for with SWIG+Java using "Directors", however it's not quite as straightforward mapping from the C++ abstract classes onto Java as you might hope. My answer therefore is split into three parts - firstly the simple example of implementing a C++ pure virtual function in Java, secondly an explanation of why the output is like that and thirdly a "work-around".
Implementing a C++ interface in Java
Given a header file (
module.hh
):We'd like to wrap this and make it work intuitively from the Java side. We can do this by defining the following SWIG interface:
Here we've enabled directors for the whole module, and then requested they be used for
class Interface
specifically. Other than that and my favourite "load the shared object automatically" code there's nothing particularly noteworthy. We can test this with the following Java class:We can then run this and see it's working as expected:
If you're happy with it being neither
abstract
nor aninterface
you can stop reading here, directors do everything you need.Why does SWIG generate a
class
instead of aninterface
?SWIG has however made what looked like an abstract class into a concrete one. That means on the Java side we could legally write
new Interface();
, which makes no sense. Why does SWIG do this? Theclass
isn't evenabstract
, let alone aninterface
(See point 4 here), which would feel more natural on the Java side. The answer is twofold:delete
, manipulating thecPtr
etc. on the Java side. That couldn't be done in aninterface
at all.Consider the case where we wrapped the following function:
Here SWIG knows nothing more about the return type than that it's of type
Interface
. In an ideal world it would know what the derived type is, but from the function signature alone there's no way for it to figure this out. This means that in the generated Java somewhere there's going to have to be a call tonew Interface
, which wouldn't be possible/legal ifInterface
were abstract on the Java side.Possible workaround
If you were hoping to provide this as an interface in order to express a type hierarchy with multiple inheritance in Java this would be quite limiting. There's a workaround however:
Manually write the interface as a proper Java interface:
Modify the SWIG interface file:
Interface
to beNativeInterface
on the Java side. (We ought to make it visible only to the package in question too, with our wrapped code living in a package of its own to avoid people doing "crazy" things.Interface
in C++ code SWIG will now be usingNativeInterface
as the type on the Java side. We need typemaps to map thisNativeInterface
in function parameters onto theInterface
Java interface we added manually.NativeInterface
as implementingInterface
to make the Java side behaviour natural and believable to a Java user.Interface
without being aNativeInterface
too.NativeInterface
still, not allInterface
s will be one though (although allNativeInterfaces
will), so we provide some glue to makeInterface
s behave asNativeInterfaces
, and a typemap to apply that glue. (See this document for a discussion of thepgcppname
)This results in a module file that now looks like:
Now we can wrap a function like:
and use it like:
This now runs as you'd hope:
And we've wrapped an abstract class from C++ as an interface in Java exactly as a Java programmer would expect!