Generating Java interface with SWIG
我使用SWIG来制作一个C++库的Java包装器(关于JSON(DE)序列化),以便在Android上使用它。我在C++中定义了一个抽象类,表示一个可以(DE)序列化的对象:
1 2 3 4 5 | class IJsonSerializable { public: virtual void serialize(Value &root) = 0; virtual void deserialize(Value &root) = 0; }; |
现在,我试着从这个类中生成一个Java接口。这是我的swig接口:
1 2 3 4 5 6 7 8 9 10 11 12 | %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这是一个接口)一个简单的类,用这两种方法和一个默认的构造函数/析构函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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生成有效的接口?
你可以用Sigg+Java使用"导演"来实现你想要的东西,但是它并不像你希望的那样直接从C++抽象类映射到Java。因此,我的答案分为三个部分:首先,在Java中实现C++纯虚拟函数的简单示例;其次,解释为什么输出是这样的,第三个是"工作"。
用Java实现C++接口给定头文件(
1 2 3 4 5 6 7 8 9 10 11 12 | #include <string> #include <iosfwd> class Interface { public: virtual std::string foo() const = 0; virtual ~Interface() {} }; inline void bar(const Interface& intf) { std::cout << intf.foo() << std::endl; } |
我们想把它包起来,让它从Java端直观地工作。我们可以通过定义以下swig接口来实现这一点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | %module(directors="1") test %{ #include <iostream> #include"module.hh" %} %feature("director") Interface; %include"std_string.i" %include"module.hh" %pragma(java) jniclasscode=%{ static { try { System.loadLibrary("module"); } catch (UnsatisfiedLinkError e) { System.err.println("Native code library failed to load. " + e); System.exit(1); } } %} |
在这里,我们为整个模块启用了控制器,然后要求它们专门用于
1 2 3 4 5 6 7 8 9 |
然后我们可以运行它并看到它按预期工作:
ajw@rapunzel:~/code/scratch/swig/javaintf > java Run
Hello from Java!
如果你对既不是
然而,Swig已经将看起来像抽象类的类变成了具体类。这意味着在Java方面,我们可以合法地编写EDCOX1×6,这是没有意义的。为什么Swig会这么做?EDOCX1的4Ω甚至不是EDCOX1×2,更不用说EDCOX1×3 3(参见这里的4点),这在Java方面会更自然。答案是双重的:
考虑我们包装以下函数的情况:
1 | Interface *find_interface(); |
在这里,Swig对返回类型的了解不比它的类型
可能的解决方法
如果您希望将此作为接口来表示Java中的多重继承的类型层次结构,这将是相当有限的。不过,还有一个解决办法:
手动编写接口作为一个合适的Java接口:
1 2 3 |
修改swig接口文件:
这会产生一个模块文件,现在看起来像:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | %module(directors="1") test %{ #include <iostream> #include"module.hh" %} %feature("director") Interface; %include"std_string.i" // (2.1) %rename(NativeInterface) Interface; // (2.2) %typemap(jstype) const Interface&"Interface"; // (2.3) %typemap(javainterfaces) Interface"Interface" // (2.5) %typemap(javain,pgcppname="n", pre=" NativeInterface n = makeNative($javainput);") const Interface& "NativeInterface.getCPtr(n)" %include"module.hh" %pragma(java) modulecode=%{ // (2.4) private static class NativeInterfaceProxy extends NativeInterface { private Interface delegate; public NativeInterfaceProxy(Interface i) { delegate = i; } public String foo() { return delegate.foo(); } } // (2.5) private static NativeInterface makeNative(Interface i) { if (i instanceof NativeInterface) { // If it already *is* a NativeInterface don't bother wrapping it again return (NativeInterface)i; } return new NativeInterfaceProxy(i); } %} |
现在我们可以包装一个函数,比如:
1 2 3 4 5 6 7 8 9 10 11 | // %inline = wrap and define at the same time %inline %{ const Interface& find_interface(const std::string& key) { static class TestImpl : public Interface { virtual std::string foo() const { return"Hello from C++"; } } inst; return inst; } %} |
使用它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import java.util.ArrayList; public class Run implements Interface { public static void main(String[] argv) { ArrayList<Interface> things = new ArrayList<Interface>(); // Implements the interface directly things.add(new Run()); // NativeInterface implements interface also things.add(test.find_interface("My lookup key")); // Will get wrapped in the proxy test.bar(things.get(0)); // Won't get wrapped because of the instanceOf test test.bar(things.get(1)); } public String foo() { return"Hello from Java!"; } } |
这就像你希望的那样运行:
ajw@rapunzel:~/code/scratch/swig/javaintf > java Run
Hello from Java!
Hello from C++
正如Java程序员所期望的那样,我们在C++中封装了一个抽象类作为Java的接口!