SWIG是什么?
SWIG(Simplified Wrapper and Interface Generator)是一个将C/C++接口转换为其他语言接口的工具,从而可以讲C/C++的库集成到其他语言的系统中。目前SWIG已经可以支持Python, Java, C#,Ruby,PHP,R语言等十多种语言。
官方网址:
Simplified Wrapper and Interface Generatorhttps://www.swig.org/
SWIG对c/c++语言特性的支持:
ISO C99全部特性, ISO C++ 从98到11 , 14, 17。 暂时还不支持C++20的特性
SWIG支持生成的语言:
- C#
- D
- Go
- Guile
- Java
- Javascript
- Lua
- MzScheme/Racket
- OCaml
- Octave
- Perl
- PHP
- Python
- R
- Ruby
- Scilab
- Tcl
支持的平台
Unix,windows,Mac都支持。
SWIG如何使用?
1.编写swig的interface文件,指明接口的内容
2.用swig程序生成对应的接口代码
3.用gcc/g++编译生成的接口代码, 以及对应语言的代码
4.对目标语言调用接口
下面我给结合Demo.
这个Demo将cpp的代码接口转化为JAVA的代码接口。
cpp代码:
apple.h
#ifndef __APPLE_H__
#define __APPLE_H__
enum class LogLevel {
Trace /// Most detailed output
,Debug
,Info
,Warn
,Error
,Fatal /// Least detailed output
,Current /// no-op, value indicates current level should be retained
};
class Apple
{
public:
Apple();
int GetColor(void);
void SetColor(int color);
private:
int m_nColor;
};
#endif
apple.cpp
#include "apple.h"
Apple::Apple() : m_nColor(0)
{
}
void Apple::SetColor(int color)
{
m_nColor = color;
}
int Apple::GetColor(void)
{
return m_nColor;
}
SWIG接口文件 apple.i
%module demo
%{
/* Includes the header in the wrapper code */
#include "apple.h"
%}
/* Parse the header file to generate wrappers */
%include "apple.h"
用SWIG生成代码
swig -java -c++ apple.i
swig会生成好几个文件:
demo.java, module名称
Apple.java, 类的实现
apple_wrap.cxx , c++的接口
demoJNI.java , JNI的接口
编译C++的接口
生成libapple_java.so
在这个so中直接将apple.o也包含进来了。
g++ -fpic -shared apple_wrap.cxx -o libapple_java.so apple.o \
-I/usr/lib/jvm/default-java/include \
-I/usr/lib/jvm/default-java/include/linux
java代码测试:
编写测试代码main.java
public class main {
public static void main(String argv[]) {
System.loadLibrary("apple_java");
Apple a = new Apple();
a.SetColor(1);
System.out.println(a.GetColor());
}
}
编译运行java代码
javac main.java
java -Djava.library.path=. main
1
#输出1
OK,输出1,运行成功。
SWIG生成的代码都有什么?
让我们看一看swig生成的代码都有什么东西。
不需要我们一个一个手写是有多爽。
apple_wrap.cxx 内容有点长,327行,我们只放核心内容。
基本上就是把Apple这个class里面的接口都给重新封装了一个函数,在这些函数里面调用了一下原来Apple自己的函数。
#ifndef SWIGEXPORT
# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
# if defined(STATIC_LINKED)
# define SWIGEXPORT
# else
# define SWIGEXPORT __declspec(dllexport)
# endif
# else
# if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY)
# define SWIGEXPORT __attribute__ ((visibility("default")))
# else
# define SWIGEXPORT
# endif
# endif
#endif
#include <jni.h>
#include <stdlib.h>
#include <string.h>
SWIGEXPORT jlong JNICALL Java_demoJNI_new_1Apple(JNIEnv *jenv, jclass jcls) {
jlong jresult = 0 ;
Apple *result = 0 ;
(void)jenv;
(void)jcls;
result = (Apple *)new Apple();
*(Apple **)&jresult = result;
return jresult;
}
SWIGEXPORT jint JNICALL Java_demoJNI_Apple_1GetColor(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {
jint jresult = 0 ;
Apple *arg1 = (Apple *) 0 ;
int result;
(void)jenv;
(void)jcls;
(void)jarg1_;
arg1 = *(Apple **)&jarg1;
result = (int)(arg1)->GetColor();
jresult = (jint)result;
return jresult;
}
SWIGEXPORT void JNICALL Java_demoJNI_Apple_1SetColor(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jint jarg2) {
Apple *arg1 = (Apple *) 0 ;
int arg2 ;
(void)jenv;
(void)jcls;
(void)jarg1_;
arg1 = *(Apple **)&jarg1;
arg2 = (int)jarg2;
(arg1)->SetColor(arg2);
}
SWIGEXPORT void JNICALL Java_demoJNI_delete_1Apple(JNIEnv *jenv, jclass jcls, jlong jarg1) {
Apple *arg1 = (Apple *) 0 ;
(void)jenv;
(void)jcls;
arg1 = *(Apple **)&jarg1;
delete arg1;
}
当然,也不能少了 java代码。
Apple.java 的内容
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
*
* Do not make changes to this file unless you know what you are doing--modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
public class Apple {
private transient long swigCPtr;
protected transient boolean swigCMemOwn;
protected Apple(long cPtr, boolean cMemoryOwn) {
swigCMemOwn = cMemoryOwn;
swigCPtr = cPtr;
}
protected static long getCPtr(Apple obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();
}
public synchronized void delete() {
if (swigCPtr != 0) {
if (swigCMemOwn) {
swigCMemOwn = false;
demoJNI.delete_Apple(swigCPtr);
}
swigCPtr = 0;
}
}
public Apple() {
this(demoJNI.new_Apple(), true);
}
public int GetColor() {
return demoJNI.Apple_GetColor(swigCPtr, this);
}
public void SetColor(int color) {
demoJNI.Apple_SetColor(swigCPtr, this, color);
}
}
还有这个 JNI的定义,这就是常见的JNI定义的方式了。
demoJNI.java
public class demoJNI {
public final static native void vv_set(long jarg1);
public final static native long vv_get();
public final static native long new_Apple();
public final static native int Apple_GetColor(long jarg1, Apple jarg1_);
public final static native void Apple_SetColor(long jarg1, Apple jarg1_, int jarg2);
public final static native void delete_Apple(long jarg1);
}
到这里你基本上就学会了如何将C++封装成JAVA。
但是还差一步,一般的java代码都是提供jar的,有些还提供多个操作系统的.so供外部调用。
java的JNI封装成jar
这一块大家可以参考:
GitHub - opentdf/client-java: Java wrapper for client-cpp core library for OpenTDF
大概是用pom.xml,将.so封装在jar里面。
加载时用的是native-lib-loader根据操作系统的不同选择不同的.so
熟悉java的可以看一下,应该也很简单。
<dependencies>
<dependency>
<groupId>org.scijava</groupId>
<artifactId>native-lib-loader</artifactId>
<version>2.1.4</version>
</dependency>
</dependencies>