当前位置: 首页 > 面试题库 >

使用SWIG生成Java接口

仰城
2023-03-14
问题内容

我正在使用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生成有效的接口?


问题答案:

您可以使用“ Directors ”使用SWIG + Java来实现所需的功能,但是它并不是您所希望的那样从C 抽象类到Java的直接映射。因此,我的答案分为三个部分-首先是在Java中实现C 纯虚函数的简单示例,其次解释了为什么输出是这样,第三是“变通方法”。

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

我们希望将其包装起来并使其从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);
    }
  }
%}

在这里,我们为整个模块启用了Director,然后要求将它们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!";
  }
}

然后,我们可以运行它并看到它正在按预期运行:

ajw @ rapunzel:〜/ code / scratch / swig / javaintf> java
从Java运行Hello!

如果你用它既不快乐abstract,也不是interface你可以停止阅读这里,导演做你需要的一切。

为什么SWIG会生成aclass而不是a interface?

但是,SWIG已将看起来像抽象类的东西变成了具体的类。这意味着在Java方面,我们可以合法地编写new Interface();,这没有任何意义。为什么SWIG会这样做?该class甚至没有abstract,更不用说interface(见点4在这里),它会觉得在Java端更自然。答案是双重的:

  1. SWIG在Java方面提供了用于调用delete,操纵cPtr等的机制。这根本不可能完成interface。
    考虑包装以下功能的情况:
Interface *find_interface();

在这里,SWIG对返回类型的了解仅是type Interface。在理想的世界中,它会知道派生的类型是什么,但是仅从函数签名中就无法解决这个问题。这意味着,在生成的Java某处有将必须要在通话new Interface,如果这是不可能的/法律Interface是在Java端抽象。

可能的解决方法

如果您希望提供此接口作为接口,以便用Java表示具有多重继承的类型层次结构,那将是相当有限的。但是有一种解决方法:

  1. 手动将接口编写为适当的Java接口:
public interface Interface {
    public String foo();
}
  1. 修改SWIG接口文件:

    1. 重命名C ++类Interface是NativeInterface在Java端。(我们也应该使它仅对相关的包可见,我们包装的代码位于自己的包中,以避免人们做“疯狂”的事情。
    2. 现在,Interface在C ++代码中到处都有SWIG,它将NativeInterface在Java端用作类型。我们需要类型映射来将NativeInterface函数参数中的映射到Interface手动添加的Java接口上。
    3. 标记NativeInterface为实现Interface以使Java端行为对Java用户而言自然而可信。
    4. 我们需要提供额外的代码,可以作为其实现Java的东西的代理行为有点Interface不是一个NativeInterface太。
    5. 我们传递给C ++的东西必须始终是NativeInterface静止的,Interface尽管不是所有s都会是一个(尽管所有NativeInterfaces都会),所以我们提供了一些胶来使Interfaces表现为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);
  }
%}

现在我们可以包装一个类似的函数:

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

现在可以按照您希望的那样运行:

ajw @ rapunzel:〜/ code / scratch / swig / javaintf> java
从Java运行Hello!
您好,C ++

而且,我们完全像Java程序员所期望的那样,将C ++中的抽象类包装为Java中的接口!



 类似资料:
  • 我开始掌握SWIG的诀窍,最新版本(v3.0)的SWIG似乎可以处理我需要的所有现成功能,包括C 11功能,但在我的director类中使用shared_ptr时,我遇到了一个障碍。 我已经能够获得来使用普通的代理类,非常好,但现在在我的董事会上,它似乎没有得到现成的支持。它给了我自动生成的类型,比如,并生成了一个中断的接口,因为它使用的类型与代理类使用的类型不同。 我有一个简单的例子来说明我正在

  • 我正在使用SWIG生成一个围绕freetype的C包装。我的目标是为WinRT生成一个带有C接口的freetype DLL,该接口将在Windows Phone 8上运行,然而,生成的C包装似乎依赖于Tcl。由于我不需要Tcl绑定,而且WinRT默认情况下不提供Tcl,我想知道是否可以以某种方式关闭Tcl依赖项。我正在使用Visual Studio 2012编译生成的代码。 我查阅了文档,只找到了

  • 问题内容: 因此,我正在尝试使用SWIG将C库(libnfc)移植到Java。 我已经准备好编译共享库,并且基本的“ nfc_version()”方法调用将起作用。但是,调用“ nfc_init()”进行设置会导致SIGSEGV错误。直接调用nfc库就可以了。 我用来生成共享库的命令: libnfc.i文件: 即,它应该包括libnfc提供的所有方法。 这是我得到的错误日志:http : //op

  • 本文向大家介绍使用DAC接口生成矩形波,包括了使用DAC接口生成矩形波的使用技巧和注意事项,需要的朋友参考一下 我们编写了一个程序来生成数模转换器(DAC)干扰的矩形接口:  让我们考虑这个领域的问题解决方案。问题指出:为了获得单极性输出,J1在接口上短接到J2。要在CRO上显示波形,请将连接器P1的引脚1连接到CRO信号引脚,并将连接器P1的引脚2连接到CRO接地引脚。 该程序说明如下。

  • 本文向大家介绍使用DAC接口生成三角波,包括了使用DAC接口生成三角波的使用技巧和注意事项,需要的朋友参考一下 我们编写了一个8085汇编语言程序,用于使用数模转换器(DAC)接口生成三角波。在CRO上可以看到波形的显示。 让我们考虑这个领域的问题解决方案。问题指出:为了获得单极性输出,J1在接口上短接到J2。要在CRO上显示波形,请将连接器P1的引脚1连接到CRO信号引脚,并将连接器P1的引脚2

  • 问题内容: 我有一些本机C ++代码,正在使用SWIG转换为Java,以便我的Java应用程序可以使用它。特别是有些函数返回std :: vector。这是我的界面文件的一个片段: 并包含在我正在使用的SWIG版本中。我的第一个惊喜是Java输出包含SWIG的Vector类的“自有”版本(与using相对)。我的真正问题是从这些函数返回的Vector似乎不起作用。例如,我无法使用(有时会使程序崩溃