Introduction and use of SWIG

Official website: https://www.swig.org/ github: https://github.com/swig

SWIG is a software development tool that interfaces programs written in C and C++ with various high-level programming languages.

SWIG works with different types of target languages, including common scripting languages ​​such as Javascript, Perl, PHP, Python, Tcl, and Ruby. The list of supported languages ​​also includes non-scripting languages ​​such as C#, D, the Go language, Java including Android, Lua, OCaml, Octave, Scilab, and R. Various interpreted and compiled Scheme implementations are also supported (Guile, MzScheme/Racket). SWIG is most commonly used to create high-level interpreted or compiled programming environments, user interfaces, and as a tool for testing and prototyping C/C++ software. SWIG is typically used to parse the C/C++ interface and generate the "glue code" required for the above target language to call the C/C++ code. SWIG can also export its parse tree as XML. SWIG is free software, and the code generated by SWIG is compatible with commercial and non-commercial projects.

Download and install:

https://www.swig.org/download.html

./configure --prefix=/usr \
            --without-maximum-compile-warnings &&
make
make install 

1、SWIG for java:

Official website: SWIG and Java

SWIG's Java extensions make it very easy to access existing C/C++ code from Java because SWIG writes the Java Native Interface (JNI) code for you. It differs from using the "javah" tool because SWIG will wrap existing C/C++ code, whereas javah takes "native" Java function declarations and creates C/C++ function prototypes. SWIG wraps C/C++ code with Java proxy classes, which is useful if you want to access a lot of C/C++ code from Java. Using SWIG may be overkill if you only need one or two JNI functions. SWIG enables Java programs to easily call C/C++ code from Java. Historically, SWIG was unable to generate any code to call Java code from C++. However, SWIG now supports full cross-language polymorphism and generates code to call from C++ to Java when wrapping a C++ virtual method.

LightGBM4j: a java wrapper for LightGBM, using SWIG.

https://github.com/metarank/lightgbm4j

1) The difference between SWIG and JNA:

2) What can SWIG for java do?

SWIG is to generate intermediate code for other programming languages ​​that can call libraries written in C and C++. For example, if you know that Java can call C and C++ through JNI, then SWIG is "doing JNI things".

Here is an explanation of "doing JNI things": when we are developing Java, in some cases we need to call a library developed by C. But there is no way for Java code to directly call the library developed by C. It needs to be developed in roughly two steps:

In the first step, you need to write a Java layer code, load the so library, and declare a native method:

//MyFunJNI.java文件,包名为: com.my.fun
public class MyFunJNI {
  static {
      System.loadLibrary("myfun");
  }
  public final static native int doFun();
}

In the second step, you need to write a C layer code corresponding to the doFun method in the first step MyFunJNI, and call the function int c_fun() in the C library you want to use:

/myjni.c
jint Java_com_my_fun_MyFunJNI_doFun(JNIEnv *env,jobject thiz){
    return c_fun();
}

Then put the myjni.c programming library, such as libmyfun.so, and put this libmyfun.so and the library written in C in a location where the java program can be loaded when it is running.

In this way, the java program can call the method in the library developed by C. In most cases, the second step of coding is a boring job of translating from Java to C, especially when it involves some types of conversion work. If you are not familiar with the writing, you have to check it yourself or copy it from elsewhere. But if SWIG is used, we don't need to write the relevant codes in the first and second steps by ourselves, because SWIG can generate them for us. Therefore, I think what SWIG does is to free you from the "heavy, boring and error-prone" work.

 2. SWIG uses:

1) Assume that the C/C++ file we want to package is as follows, named example.c:

#include <time.h>
 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(&ltime);
     return ctime(&ltime);
 }

2) In addition, we also need to define an interface definition script i file (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) Execute the SWIG command to generate the file corresponding to java:

swig -java example.i

The following two java files are generated: 

Check:

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();
}

From the perspective of JAVA, the encapsulation of the interface has been completed, but the native method needs to call the corresponding dynamic library to package through the following command. Note that this is a pit point. The following commands cannot be copied, because the following commands are compiled through the dynamic library in the include directory of the local JAVA environment, and different environments need to be changed to suitable paths.

4) Compile with 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

Generated example.o and example_Wrap.o

5) Generate a dynamic link library:

gcc -shared -o example.so example.o example_wrap.o

generated example.so

6) Call in java:

Copy the two java files (example.java and example_JNI.java) generated by SWIG to the java project, then:

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());
   }
 }

Note that there are also pitfalls here. There are two ways to load the dynamic library. One is to load the file in the absolute path by System.load, and the other is to load the default path of the system library by System.loadLibrary.

First experience with SWIG - Zhihu

Guess you like

Origin blog.csdn.net/liuxiao723846/article/details/131216385