Percy

修改SWIG接口文件以支持C void *和结构返回类型

java

我正在使用SWIG为大量的C API生成我的JNI层,我想知道以下情况的最佳实践是什么。以下内容不仅适用于SWIG,而且适用于一般JNI。

当C函数返回指向Structures的指针时,应该大量使用SWIG接口文件(JNI逻辑)还是应该创建C包装函数以分段返回数据(即,包含各种数据元素的char数组)?当C函数返回void *时,是否应该修改C API以返回实际的数据类型,无论是原始类型还是结构类型?我不确定是否要添加大量逻辑并创建中间层(SWIG接口文件/ JNI逻辑)。有什么想法吗?


阅读 438

收藏
2020-12-06

共1个答案

一尘不染

过去,我的处理方法是编写尽可能少的代码以使其工作。当我必须编写代码以使其工作时,请按以下优先顺序进行编写:

  1. 在原始库中以C或C 编写-每个人都可以使用此代码,而不必编写任何特定于Java或SWIG的东西(例如,在C 中添加更多重载,在C中添加更多版本的函数,使用SWIG知道的返回类型关于他们)

  2. 写更多的目标语言-提供“胶水”以将库的某些部分放在一起。在这种情况下,它将是Java。

完全是SWIG之外的“纯” Java,还是作为SWIG接口文件的一部分,从我的角度来看,这并不重要。Java界面的用户应该无法区分两者。但是,在许多情况下,您都可以使用SWIG来避免重复。

  1. 通过SWIG类型图编写一些JNI。这很丑陋,如果您不熟悉它,容易出错,难以维护(可以说),并且仅对SWIG + Java有用。使用SWIG类型映射至少确实意味着您为每种包装类型只编写一次。

我将这个时间胜过2的次数是以下一项或多项:

  1. 大量出现时(节省重复编码)
  2. 我根本不了解目标语言,在这种情况下,使用该语言的C API可能比用该语言编写东西要容易
  3. 用户会期望的
  4. 否则就无法使用以前的样式。
    基本上,我建议的这些准则是尝试将功能交付给尽可能多的库用户,同时最大程度地减少您必须编写的额外的,目标语言特定的代码,并减少必须编写时的复杂性。

对于以下特定情况sockaddr_in*

方法1

我要尝试做的第一件事是避免包装除指向它的指针之外的任何东西。这是swig在默认情况下所做的SWIGTYPE_p_sockaddr_in事情。如果您所要做的只是将它从一件事传递到另一件事,以容器的形式存储/作为成员等,则可以很高兴地在Java中使用这种“未知”类型。

public static void main(String[] argv) {
  Module.takes_a_sockaddr(Module.returns_a_sockaddr());
}

如果这样做不能解决问题,则可以使用C编写另一个函数,例如:

const char * sockaddr2host(struct sockaddr_in *in); // Some code to get the host as a string
unsigned short sockaddr2port(struct sockaddr_in *in); // Some code to get the port

不过,在这种情况下,这不是很好-使用地址族(您可能会避免使用sockaddr_in)来处理地址族(这就是您为什么要首先使用它的原因)有一定的复杂性,但这并不是特定于Java的,它并不是晦涩的语法,除此之外,这一切都会自动为您完成。

方法2

如果那还不够好,那么我将开始考虑编写一些Java-您可以通过将SWIGTYPE_p_sockaddr_in类型隐藏为自己的Java类型的私有成员,并包装对函数的调用来公开一个更好的接口在某种为您构造类型的Java中返回它,例如

public class MyExtension {
  private MyExtension() { }
  private SWIGTYPE_p_sockaddr_in detail;
  public static MyExtension native_call() {
    MyExtension e = new MyExtension();
    e.detail = Module.real_native_call();
    return e;
  }

  public void some_call_that_takes_a_sockaddr() {
    Module.real_call(detail);
  }
}

无需编写额外的SWIG,无需编写JNI。您可以通过使用SWIG来做到这一点,%pragma(modulecode)以使其在SWIG实际生成的模块上全部重载-对于Java用户来说,这似乎更自然(这看起来并不特殊),并且实际上也没有任何复杂之处。SWIG仍然在努力,这只是为了避免在Java端重复编码而提供了一些技巧。

方法3

这基本上是我先前回答的第二部分。很好,因为它看起来和感觉都是Java用户所固有的,并且C库也不必修改。本质上,类型映射提供了一种干净的语法,用于封装JNI调用,以将Java用户期望的内容转换为C的工作方式,并且双方都不了解对方的前景。

缺点是,它较难维护且确实很难调试。我的经验是,SWIG在类似这样的事情上有一个陡峭的学习曲线,但是一旦达到一个点,编写类型图就不需要花费太多精力,像这样,它们就可以通过重用和封装C而为您提供强大的功能type-> Java类型映射非常有用且功能强大。

如果您是团队中的一员,但只有一个真正了解SWIG界面的人,那么这就提出了一个很大的“如果您被公交车撞到怎么办?” 影响整个项目。(不过,这可能会让您不愉快!)

2020-12-06