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

时间:2011-12-06 18:25:13

标签: java-native-interface swig

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

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

1 个答案:

答案 0 :(得分:2)

我过去对此的处理方法是尽可能少编写代码以使其正常工作。当我必须编写代码使其工作时,我按照优先顺序编写它:

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

  2. 撰写更多目标语言 - 供应"胶水"把图书馆的一些部分放在一起。在这种情况下,这将是Java。

    如果这是纯粹的"那真的不重要。 Java,完全在SWIG之外,或者从我的角度看作为SWIG接口文件的一部分。 Java接口的用户不应该能够区分这两者。您可以使用SWIG帮助避免在许多情况下重复。

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

    我支持2岁以上的时间是以下一项或多项:

    1. 当它出现很多时(节省重复编码)
    2. 我根本不了解目标语言,在这种情况下,使用语言的C API可能比用该语言写一些东西更容易
    3. 用户会期待这个
    4. 或者只是不可能使用以前的样式。
  4. 基本上我建议的这些指导方针是尽可能为尽可能多的库用户提供功能,同时最大限度地减少必须编写的额外目标语言特定代码的数量,并在必须编写代码时降低其复杂性


    针对sockaddr_in*的特定情况:

    方法1

    我尝试做的第一件事是避免包装任何东西而不是指向它的指针。这就是swig默认使用SWIGTYPE_p_sockaddr_in做的事情。你可以使用这个"未知"如果您所做的只是将它从一件事传递到另一件事,以容器/作为成员等存储,例如。

    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用户来说感觉更自然(它看起来不像特殊情况)并且不是#39}真的更复杂。 SWIG正在努力完成这项工作,这只是提供了一些避免在Java方面重复编码的优点。

    方法3

    这基本上是my previous answer的第二部分。它很好,因为它看起来和感觉是Java用户原生的,并且C库也不必修改。从本质上讲,typemap提供了一个干净的语法,用于封装JNI调用,以便从Java用户期望的转换到C的工作,并且双方都不了解对方的前景。

    但缺点是它难以维护并且很难调试。我的经验是,SWIG对于这样的事情有着陡峭的学习曲线,但是一旦你达到了这样的程度,它就不会花费太多精力来编写类似地图,就像它们通过重复使用和封装一样给你带来的力量。 C类型> Java类型映射非常有用且功能强大。

    如果您是团队中的一员,但是唯一真正了解SWIG界面的人那么,如果您被公共汽车撞到了那么会是什么?""整个项目的因素。 (虽然可能会让你不受欢迎!)