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

发布于 2021-01-29 19:37:41

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

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

关注者
0
被浏览
107
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

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

    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界面的人,那么这就提出了一个很大的“如果您被公交车撞到怎么办?” 影响整个项目。(不过,这可能会让您不愉快!)



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看