官网:https://www.swig.org/ github:https://github.com/swig
SWIG 是一种软件开发工具,可将用 C 和 C++ 编写的程序与各种高级编程语言连接起来。
SWIG 与不同类型的目标语言一起使用,包括常见的脚本语言,如 Javascript、Perl、PHP、Python、Tcl 和 Ruby。支持的语言列表还包括非脚本语言,例如 C#、D、Go 语言、Java,包括 Android、Lua、OCaml、Octave、Scilab 和 R。还支持多种解释和编译的 Scheme 实现(Guile、MzScheme/Racket) . SWIG 最常用于创建高级解释或编译的编程环境、用户界面,以及作为测试和原型设计 C/C++ 软件的工具。 SWIG 通常用于解析 C/C++ 接口并生成上述目标语言调用 C/C++ 代码所需的“胶水代码”。 SWIG 还可以以 XML 的形式导出其解析树。 SWIG 是免费软件,SWIG 生成的代码与商业和非商业项目兼容。
下载、安装:
https://www.swig.org/download.html
./configure --prefix=/usr \
--without-maximum-compile-warnings &&
make
make install
官网:SWIG and Java
SWIG 的 Java 扩展使得从 Java 访问现有 C/C++ 代码变得非常容易,因为 SWIG 为您编写了 Java 本机接口 (JNI) 代码。它与使用“javah”工具不同,因为 SWIG 将包装现有的 C/C++ 代码,而 javah 采用“本机”Java 函数声明并创建 C/C++ 函数原型。 SWIG 使用 Java 代理类包装 C/C++ 代码,如果您想从 Java 访问大量 C/C++ 代码,它非常有用。如果只需要一个或两个 JNI 函数,那么使用 SWIG 可能有点矫枉过正。 SWIG 使 Java 程序能够轻松地从 Java 调用 C/C++ 代码。从历史上看,SWIG 无法生成任何代码来从 C++ 调用 Java 代码。但是,SWIG 现在支持完整的跨语言多态性,并且在包装 C++ 虚拟方法时生成代码以从 C++ 调用到 Java。
LightGBM4j: a java wrapper for LightGBM,使用的就是SWIG。
https://github.com/metarank/lightgbm4j
1)SWIG和JNA的区别:
2)SWIG for java能做什么?
SWIG就是要为其他编程语言生成 能够调用 C和C++编写的库 的中间代码。 举个例子,如果你知道Java 可以通过JNI 调用C和C++,那么SWIG也就是 “做JNI的事情”。
这里先解释一下 “做JNI的事情”:当我们在进行Java开发时候,在某些情况下是要调用C开发的库。但Java代码是没办法直接调用C开发的库,需要做大致两个步骤的开发才可行:
第一步,你要写一个Java层的代码,加载so库,以及声明一个native方法:
//MyFunJNI.java文件,包名为: com.my.fun
public class MyFunJNI {
static {
System.loadLibrary("myfun");
}
public final static native int doFun();
}
第二步,你要编写一个C层代码,对应第一步MyFunJNI中的doFun方法,并调用到你想要使用的C开发的库中的函数 int c_fun():
/myjni.c
jint Java_com_my_fun_MyFunJNI_doFun(JNIEnv *env,jobject thiz){
return c_fun();
}
然后将myjni.c编程库,比如libmyfun.so,将这个libmyfun.so和C编写的库,放在java程序运行时能加载到的位置。
这样java程序就能调用C开发的库中的方法了。在大部分情况下,第二步的代码编写是一个从Java翻译成C的枯燥无味的工作,尤其是里面涉及一些类型的转换工作,写的不熟悉的话还得自己动手查或者从别处copy。但如果是使用了SWIG,那就不需要我们自己动手写第一步和第二步中的相关代码了,因为SWIG可以帮我们生成。因此,我觉得SWIG做的事情,就是将你从“沉重枯燥易出错”的工作中解脱出来。
1)假设我们要封装的C/C++文件如下,命名为example.c :
#include
double My_variable = 3.0;
int fact(int n) {
if (n <= 1) return 1;
else return n*fact(n-1);
}
int my_mod(int x, int y) {
return (x%y);
}
char *get_time()
{
time_t ltime;
time(<ime);
return ctime(<ime);
}
2)另外,我们还要定义一个接口定义脚本i文件(example.i):
%module example
%{
/* Put header files here or function declarations like below */
extern double My_variable;
extern int fact(int n);
extern int my_mod(int x, int y);
extern char *get_time();
%}
extern double My_variable;
extern int fact(int n);
extern int my_mod(int x, int y);
extern char *get_time();
3)执行SWIG命令生成java对应的文件:
swig -java example.i
生成了如下两个java文件:
查看:
public class example {
public static void setMy_variable(double value) {
exampleJNI.My_variable_set(value);
}
public static double getMy_variable() {
return exampleJNI.My_variable_get();
}
public static int fact(int n) {
return exampleJNI.fact(n);
}
public static int my_mod(int x, int y) {
return exampleJNI.my_mod(x, y);
}
public static String get_time() {
return exampleJNI.get_time();
}
}
public class exampleJNI {
public final static native void My_variable_set(double jarg1);
public final static native double My_variable_get();
public final static native int fact(int jarg1);
public final static native int my_mod(int jarg1, int jarg2);
public final static native String get_time();
}
从JAVA的角度讲,接口的封装已经完成,但native方法要调用相应的动态库要通过以下命令来打包。注意,这里是坑点。以下命令不能照搬,因为以下命令是要通过本地的JAVA环境的include目录中的动态库来进行编译,不同的环境要自己改成适合的路径。
4)gcc编译:
gcc -c -fPIC example.c example_wrap.c -I/usr/lib/jvm/jdk-11.0.9/include -I/usr/lib/jvm/jdk-11.0.9/include/linux
生成了example.o 和 example_Wrap.o
5)生成动态链接库:
gcc -shared -o example.so example.o example_wrap.o
生成了example.so
6)java中调用:
将SWIG生成的两个java文件(example.java和example_JNI.java)拷贝到java工程中,然后:
public class main {
public static void main(String argv[]) {
System.load("/home/kevin/Documents/Cpp/example.so");
System.out.println(example.getMy_variable());
System.out.println(example.fact(5));
System.out.println(example.get_time());
}
}
注意这里也有坑,动态库的加载有两种方法,一是System.load加载绝对路径中的文件,一是System.loadLibrary加载系统库的默认路径。
SWIG初体验 - 知乎