使用SWIG生成Java界面 [英] Generating Java interface with SWIG

查看:213
本文介绍了使用SWIG生成Java界面的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用SWIG制作一个C ++库(关于Json(de)序列化)的Java包装器在Android上使用它。我在C ++中定义了一个抽象类,表示一个可以被(de)序列化的对象:

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; 
};

现在,我试图从这个类生成一个Java接口。这是我的SWIG界面:

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;
};

但是生成的Java代码是(显然,告诉SWIG这是一个接口)一个简单的类,有两个方法和一个默认的构造函数/析构函数:

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);
  }  

}

如何使用SWIG生成有效的接口? / p>

How can I generate a valid interface with SWIG ?

推荐答案

您可以使用导向器,但是它不是从C ++抽象类到Java可能希望的直接映射。因此,我的答案分为三部分 - 首先是在Java中实现C ++纯虚函数的简单例子,其次解释为什么输出是这样的,第三是一个工作。

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".

给定一个头文件( module.hh ) :

Given a header file (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;
}

我们想要将它包装起来, 。我们可以通过定义以下SWIG界面来实现这一点:

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:

%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);
    }
  }
%}

导致整个模块,然后请求它们用于 class Interface 具体。除此之外,我最喜欢的加载共享对象自动代码没有什么特别值得注意。我们可以用下面的Java类来测试:

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:

public class Run extends Interface {
  public static void main(String[] argv) {
    test.bar(new Run());       
  }

  public String foo() {
    return "Hello from Java!";
  }
}

然后我们可以运行它, :

We can then run this and see it's working as expected:


ajw @ rapunzel:〜/ code / scratch / swig / javaintf> java运行

Hello!

ajw@rapunzel:~/code/scratch/swig/javaintf > java Run
Hello from Java!

如果您对此感到满意,既不是抽象

If you're happy with it being neither abstract nor an interface you can stop reading here, directors do everything you need.

类成一个具体的。这意味着在Java端我们可以合法地写 new Interface(); ,这没有意义。为什么SWIG这样做? class 甚至不是 abstract ,更不用说接口(请参阅第4点此处),这在Java端感觉更自然。答案是双重的:

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? The class isn't even abstract, let alone an interface (See point 4 here), which would feel more natural on the Java side. The answer is twofold:


  1. SWIG提供调用 delete 的机制, code> cPtr 等。这在接口中完全不能实现。

  2. 考虑下面的情况: / p>

  1. SWIG supplies mechanics for calling delete, manipulating the cPtr etc. on the Java side. That couldn't be done in an interface at all.
  2. Consider the case where we wrapped the following function:

Interface *find_interface();

在这里SWIG知道没有什么更多的返回类型比它的类型接口。在理想的世界里,它会知道派生类型是什么,但是从函数签名本身就没有办法解决这个问题。这意味着在生成的Java 某处,必须调用新接口,这将是不可能的/ legal if 接口在Java端是抽象的。

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 to new Interface, which wouldn't be possible/legal if Interface were abstract on the Java side.



/ h1>

如果你希望提供这个接口为了在Java中表达一个具有多重继承的类型层次结构,这将是非常有限的。但是有一个解决方法:

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:


  1. 将接口手动编写为一个合适的Java接口:

  1. Manually write the interface as a proper Java interface:

public interface Interface {
    public String foo();
}


  • 修改SWIG界面文件:

  • Modify the SWIG interface file:


    1. 重命名C ++类接口 NativeInterface Java端。 (我们应该让它只能看到有关的包,我们的包装的代码生活在自己的包,以避免人们做疯狂的东西。

    2. 到处我们有一个在C ++代码中的接口 SWIG现在将使用 NativeInterface 作为Java端的类型,我们需要类型映射 NativeInterface 在我们手动添加的接口 Java接口中。

    3. 标记 NativeInterface 作为实现接口,使Java端行为对Java用户是自然和可信的。

    4. 我们需要提供一些额外的代码,它们可以作为实现Java 接口的代理,而不是 NativeInterface

    5. 传递给C ++的内容必须始终是 NativeInterface code>接口将是一个虽然(虽然所有 NativeInterfaces 将),所以我们提供一些胶水使 Interface 的行为如 NativeInterfaces ,以及一个应用该粘合的typemap。 (有关 pgcppname 此文档 $ c>)

    1. Rename the C++ class Interface to be NativeInterface 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.
    2. Everywhere we have an Interface in C++ code SWIG will now be using NativeInterface as the type on the Java side. We need typemaps to map this NativeInterface in function parameters onto the Interface Java interface we added manually.
    3. Mark NativeInterface as implementing Interface to make the Java side behaviour natural and believable to a Java user.
    4. We need to supply a little bit of extra code that can act as a proxy for things which implement the Java Interface without being a NativeInterface too.
    5. What we pass to C++ must always be a NativeInterface still, not all Interfaces will be one though (although all NativeInterfaces will), so we provide some glue to make Interfaces behave as NativeInterfaces, and a typemap to apply that glue. (See this document for a discussion of the pgcppname)

    这将产生一个模块文件,如下所示:

    This results in a module file that now looks like:

    %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);
      }
    %}
    


  • 现在我们可以包装一个函数:

    Now we can wrap a function like:

    // %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;
      }
    %}
    

    并使用它:

    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!";
      }
    }
    

    p>

    This now runs as you'd hope:


    ajw @ rapunzel:〜/ code / scratch / swig / javaintf> javaRun└
    Hello from Java! />
    C ++中的Hello

    ajw@rapunzel:~/code/scratch/swig/javaintf > java Run
    Hello from Java!
    Hello from C++

    我们已经将来自C ++的抽象类作为Java中的接口Java程序员会期望!

    And we've wrapped an abstract class from C++ as an interface in Java exactly as a Java programmer would expect!

    这篇关于使用SWIG生成Java界面的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆