一、跨平台调用
在开发过程中,尤其是对外提供的类库,往往需要跨平台/跨语言支持,跨平台的优势很明显,开发成本极大降低,测试成本也相应降低,多端逻辑对齐等。所以有很多跨平台方案。
Web 流派:PhoneGap
- 优点
- 支持 UI 开发
- 跨平台:PhoneGap 目前支持的移动平台有:Android, iOS, Windows Phone, Windows 8, Firefox OS, Amazon Fire OS, BlackBerry 10, Ubuntu, Tizen
- 缺点
- 运行效率慢
- PhoneGap 支持 API 调用范围有限
代码转换流派:
- 语言之间转换:Java-OC(J2ObjC)
- Haxe:中间语言,本身不能编译运行,只能转换为其他语言,支持 JavaScript, C++, C#, Java, JVM, Python, Lua, PHP, Flash 多种语言,以对应语言原生代码运行
编译流派:C/C++,Go
- Golang(gobind)
- C/C++(java: jni,go: cgo, python: ctypes)
二、SWIG 是什么
C/C++库提供给不同平台和不同语言使用,存在的一些问题
- 适配层工作量较大:取决于 API 数量
- 易错:涉及底层数据类型的转换
- 不同语言实现不同:如 Android 平台使用,就需要提供 Java 接口的支持,可以通过实现 JNI 胶水层连接;提供 Go 语言使用,需要 cgo 等
SWIG 就是为了解决跨语言调用的问题产生的工具,考虑到胶水层的形式固定,有利于通过代码自动生成,SWIG 就应运而生,作为一种提升效率的开发工具,它将用 C 和 C++ 编写的程序与各种高级编程语言连接起来。
三、SWIG 能做什么
自动生成 C/C++代码不同高级语言的封装(胶水层),支持的语言 :C#, D, Go, Guile, Java, Javascript, Lua, MzScheme/Racket, OCaml, Octave, Perl, PHP, Python, R, Ruby, Scilab, Tcl
支持 ISO C99:所有的数据类型,指针、枚举、结构体、数组、typedef 等 支持 ISO C++98 to C++17:类,继承和多重继承,重载、静态方法、名字空间、模板、嵌套类、STL 等
文档生成:支持 Doxygen 注释生成对应目标语言的文档(Java、Python)
四、Show 一些例子
1. Java 调用 C 最简单例子
// File: tools.c
int TestFunction(int a, int b) {
return a + b;
}
// File: tools.i
%module test
extern int TestFunction(int a, int b);
// File: main.java
public class main {
public static void main(String argv[]) {
System.load("/home/bo.deng/libtest.so");
System.out.println(test.TestFunction(1, 3));
}
}
编译命令
swig -java tools.i
gcc -c -fPIC tools.c tools_wrap.c -I/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.322.b06-1.1.alios7.x86_64/include -I/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.322.b06-1.1.alios7.x86_64/include/linux/
gcc -shared tools.o tools_wrap.o -o libtest.so
javac main.java
java main
2. C++ 回调
// File: include/shape.h
#ifndef SHAPE_H
#define SHAPE_H
#include <string>
struct Param
{
std::string one;
std::string two;
};
class Shape {
public:
virtual void draw(const Param & param) = 0;
virtual ~Shape() {}
};
class Painter {
public:
void drawShape(Shape * shape);
};
#endif // SHAPE_H
// File: src/shape.cpp
#include "shape.h"
#include <stdio.h>
#include <unistd.h>
void Painter::drawShape(Shape * shape)
{
for (int i = 0; i < 100; i++) {
Param p;
p.one = "testddd";
p.two = "ddd";
shape->draw(p);
printf("call java over\n");
sleep(1);
}
}
// File: shape.i
%module(directors="1") drawing
%feature("director") Shape;
%{
#include "include/shape.h"
%}
%include "std_string.i"
%include "include/shape.h";
director 指令允许目标语言从当前类派生。
// File: main.java
import com.test.shape.Shape;
import com.test.shape.Painter;
import com.test.shape.Param;
class Circle extends Shape {
public void draw(Param param) {
System.out.println(param.getOne());
System.out.println(param.getTwo());
System.out.println("drawing a circle");
}
}
public class main {
public static void main(String argv[]) {
System.load(System.getProperty("user.dir") + "/libdrawing.so");
System.out.println("start...");
new Thread() {
public void run() {
Painter painter = new Painter();
painter.drawShape(new Circle());
}
}.start();
System.out.println("end...");
try {
for (int i = 0; i < 100; i++) {
System.out.println("main thread");
Thread.sleep(1000);
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
编译
java:
mkdir -p com/test/shape/
swig -c++ -java -package com.test.shape -outdir com/test/shape/ shape.i
lib:java
g++ -fPIC -c src/shape.cpp -Iinclude
g++ -fPIC -c shape_wrap.cxx -I/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.322.b06-1.1.alios7.x86_64/include -I/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.322.b06-1.1.alios7.x86_64/include/linux/
g++ -shared shape.o shape_wrap.o -o libdrawing.so
test:lib
mkdir jar
javac -d jar/ -sourcepath . com/test/shape/*.java
jar cf drawing.jar -C jar/ .
javac -classpath drawing.jar main.java
run:clean test
java -classpath drawing.jar:. main
clean:
rm -rf com
rm -rf jar
rm -f shape_wrap.cxx
rm -f shape_wrap.h
rm -f *.o
rm -f *.so
rm -f *.jar
rm -f *.class
rm -f drawing_wrap.*
3. Go 语言调用
// File: main.go
package main
import "./drawing"
import "log"
type Cicle struct{
}
func (c *Cicle) Draw(p drawing.Param) {
log.Println("callback:", p)
log.Println(p.GetOne())
log.Println(p.GetTwo())
}
func main() {
cicle := &Cicle{}
log.Println("cicle is ", cicle)
painter := drawing.NewPainter()
shape := drawing.NewDirectorShape(cicle)
painter.DrawShape(shape)
drawing.DeleteShape(shape)
drawing.DeletePainter(painter)
}
mkdir drawing
swig -go -cgo -c++ -intgosize 64 -outdir drawing shape.i
GO111MODULE=off go run main.go
C/C++代码不用修改,跟 Java 的区别在于 Go 应用层逻辑 以及 编译命令不同。
五、其他高级功能
参阅文档:https://www.swig.org/Doc4.0/Contents.html#Contents