我正在使用SWIG为大量的C API生成我的JNI层,我想知道以下情况的最佳实践是什么。以下内容不仅适用于SWIG,而且适用于一般JNI。
当C函数返回指向Structures的指针时,应该大量使用SWIG接口文件(JNI逻辑)还是应该创建C包装函数以分段返回数据(即,包含各种数据元素的char数组)?当C函数返回void *时,是否应该修改C API以返回实际的数据类型,无论是原始类型还是结构类型?我不确定是否要添加大量逻辑并创建中间层(SWIG接口文件/ JNI逻辑)。有什么想法吗?
过去,我的处理方法是编写尽可能少的代码以使其工作。当我必须编写代码以使其工作时,请按以下优先顺序进行编写:
在原始库中以C或C 编写-每个人都可以使用此代码,而不必编写任何特定于Java或SWIG的东西(例如,在C 中添加更多重载,在C中添加更多版本的函数,使用SWIG知道的返回类型关于他们)
写更多的目标语言-提供“胶水”以将库的某些部分放在一起。在这种情况下,它将是Java。
完全是SWIG之外的“纯” Java,还是作为SWIG接口文件的一部分,从我的角度来看,这并不重要。Java界面的用户应该无法区分两者。但是,在许多情况下,您都可以使用SWIG来避免重复。
我将这个时间胜过2的次数是以下一项或多项:
对于以下特定情况sockaddr_in*:
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界面的人,那么这就提出了一个很大的“如果您被公交车撞到怎么办?” 影响整个项目。(不过,这可能会让您不愉快!)