SWIG介绍和使用

官网: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 

1、SWIG for java:

官网: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的区别:

SWIG介绍和使用_第1张图片

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做的事情,就是将你从“沉重枯燥易出错”的工作中解脱出来。

 2、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文件: 

SWIG介绍和使用_第2张图片

查看:

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初体验 - 知乎

你可能感兴趣的:(java,java,开发语言)