Mar*_*say 21 c++ java java-native-interface swig
我正在使用SWIG制作一个C++库的Java包装器(关于Json(de)序列化)以在Android上使用它.我在C++中定义了一个抽象类,表示可以(反)序列化的对象:
class IJsonSerializable {
public:
virtual void serialize(Value &root) = 0;
virtual void deserialize(Value &root) = 0;
};
Run Code Online (Sandbox Code Playgroud)
现在,我正在尝试从这个类生成一个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;
};
Run Code Online (Sandbox Code Playgroud)
但是生成的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);
}
}
Run Code Online (Sandbox Code Playgroud)
如何使用SWIG生成有效的界面?
Fle*_*exo 50
您可以使用" Directors " 通过SWIG + Java实现您所需的功能,但是您可能希望将C++抽象类映射到Java上并不是那么简单.因此,我的答案分为三个部分 - 首先是在Java中实现C++纯虚函数的简单示例,其次是输出为何如此,第三是"解决方法"的解释.
给定头文件(module.hh
):
#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;
}
Run Code Online (Sandbox Code Playgroud)
我们想把它包装起来,让它从Java端直观地工作.我们可以通过定义以下SWIG接口来实现:
%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. \n" + e);
System.exit(1);
}
}
%}
Run Code Online (Sandbox Code Playgroud)
在这里,我们为整个模块启用了导向器,然后请求将它们class Interface
专门用于.除此之外,我最喜欢的"自动加载共享对象"代码没有什么特别值得注意的.我们可以使用以下Java类来测试它:
public class Run extends Interface {
public static void main(String[] argv) {
test.bar(new Run());
}
public String foo() {
return "Hello from Java!";
}
}
Run Code Online (Sandbox Code Playgroud)
然后我们可以运行它并看到它按预期工作:
ajw @ rapunzel:〜/ code/scratch/swig/javaintf> java
从Java 运行Hello!
如果你喜欢它既不abstract
也不是interface
你可以停止阅读这里,导演你需要的一切.
class
而不是interface
?然而,SWIG将看起来像抽象类的东西变成了具体的类.这意味着在Java方面我们可以合法地写new Interface();
,这没有任何意义.为什么SWIG会这样做?这class
不是偶数abstract
,更不用说interface
(参见这里的第4点),这在Java方面会更自然.答案是双重的:
delete
,操作cPtr
等的机制.根本无法做到这一点interface
.考虑我们包装以下函数的情况:
Interface *find_interface();
Run Code Online (Sandbox Code Playgroud)
这里痛饮知道没有什么更多的返回类型比,它的类型Interface
.在理想的世界中,它会知道派生类型是什么,但仅从功能签名中就没有办法解决这个问题.这意味着在生成的Java 某处必须要进行调用new Interface
,如果Interface
在Java端是抽象的,这将是不可能/合法的.
如果您希望将此作为接口提供,以便在Java中表达具有多重继承的类型层次结构,那么这将是非常有限的.但是有一个解决方法:
手动将接口编写为适当的Java接口:
public interface Interface {
public String foo();
}
Run Code Online (Sandbox Code Playgroud)修改SWIG接口文件:
Interface
是NativeInterface
在Java端.(我们应该只对有问题的包提供它,我们的包装代码生活在一个独立的包中,以避免人们做"疯狂"的事情.Interface
在C++代码中的每个地方都将使用SWIG NativeInterface
作为Java端的类型.我们需要使用类型映射将NativeInterface
函数参数映射到Interface
我们手动添加的Java接口.NativeInterface
为实现Interface
使Java端行为自然且对Java用户可信.Interface
而不是一样NativeInterface
.NativeInterface
静止的,不是所有Interface
的都是一个(尽管一切NativeInterfaces
都会),所以我们提供了一些胶水来使其Interface
表现为NativeInterfaces
,并且一个类型图应用了这个胶水.(有关讨论,请参阅此文档pgcppname
)这导致模块文件现在看起来像:
%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);
}
%}
Run Code Online (Sandbox Code Playgroud)现在我们可以包装一个函数:
// %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;
}
%}
Run Code Online (Sandbox Code Playgroud)
并使用它像:
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!";
}
}
Run Code Online (Sandbox Code Playgroud)
这现在按照您的希望运行:
ajw @ rapunzel:〜/ code/scratch/swig/javaintf> java
从Java 运行Hello!
你好,来自C++
我们已经将C++中的抽象类作为Java中的接口包装,就像Java程序员所期望的那样!