草庐IT

java - 使用 SWIG 生成 Java 接口(interface)

coder 2023-05-18 原文

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

class IJsonSerializable {
public:
    virtual void serialize(Value &root) = 0; 
    virtual void deserialize(Value &root) = 0; 
};

现在,我试图从这个类生成一个 Java 接口(interface)。这是我的 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 这是一个接口(interface))一个简单的类,具有两个方法和一个默认的构造函数/析构函数:
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 生成有效接口(interface)?

最佳答案

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

在 Java 中实现 C++ 接口(interface)

给定一个头文件( 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 接口(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 类对此进行测试:
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 Run
Hello from Java!



如果您对两者都不满意 abstract也不是 interface你可以在这里停止阅读,导演会做你需要的一切。

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

然而,SWIG 将看似抽象的类变成了具体的类。这意味着在 Java 方面我们可以合法地写 new Interface(); ,这没有任何意义。为什么 SWIG 会这样做? class甚至不是 abstract ,更不用说 interface (参见第 4 here 点),这在 Java 方面会感觉更自然。答案是双重的:
  • SWIG 提供调用机制 delete ,操纵cPtr等在Java方面。这是无法在 interface 中完成的根本。
  • 考虑我们包装以下函数的情况:
    Interface *find_interface();
    

    这里 SWIG 对返回类型一无所知,只知道它的类型是 Interface .在理想的世界中,它会知道派生类型是什么,但仅从函数签名来看,它无法弄清楚这一点。这意味着在生成的 Java 中的某个地方必须调用 new Interface。 ,如果 Interface,这是不可能的/合法的在 Java 方面是抽象的。

  • 可能的解决方法

    如果您希望将其作为接口(interface)提供以在 Java 中表达具有多重继承的类型层次结构,这将是非常有限的。但是有一个解决方法:
  • 手动将接口(interface)编写为合适的 Java 接口(interface):
    public interface Interface {
        public String foo();
    }
    
  • 修改 SWIG 接口(interface)文件:
  • 重命名 C++ 类 Interface成为 NativeInterface在 Java 方面。 (我们也应该让它只对有问题的包可见,我们包装的代码位于自己的包中,以避免人们做“疯狂”的事情。
  • 我们到处都有Interface在 C++ 代码中 SWIG 现在将使用 NativeInterface作为 Java 端的类型。我们需要类型映射来映射这个 NativeInterface在函数参数上Interface我们手动添加的Java接口(interface)。
  • 马克 NativeInterface作为实现 Interface使 Java 端的行为对 Java 用户来说是自然而可信的。
  • 我们需要提供一些额外的代码来充当实现 Java 的事物的代理 Interface没有成为 NativeInterface也。
  • 我们传递给 C++ 的内容必须始终是 NativeInterface仍然,并非所有 Interface s 将是一个(尽管所有 NativeInterfaces 都会),所以我们提供了一些胶水来制作 Interface s 表现为 NativeInterfaces , 以及应用该胶水的类型图。 (有关 pgcppname 的讨论,请参阅 this document)

  • 这会产生一个模块文件,现在看起来像:
    %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 Run
    Hello from Java!
    Hello from C++



    我们已经将 C++ 中的抽象类封装为 Java 中的接口(interface),正如 Java 程序员所期望的那样!

    关于java - 使用 SWIG 生成 Java 接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8168517/

    有关java - 使用 SWIG 生成 Java 接口(interface)的更多相关文章

    1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

      我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

    2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

      我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

    3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

      类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

    4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

      很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

    5. ruby - 在 Ruby 中使用匿名模块 - 2

      假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

    6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

      我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

    7. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

      关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

    8. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

      我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

    9. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

      我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

    10. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

      我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

    随机推荐