SWIG 跨语言调用

一、跨平台调用

在开发过程中,尤其是对外提供的类库,往往需要跨平台/跨语言支持,跨平台的优势很明显,开发成本极大降低,测试成本也相应降低,多端逻辑对齐等。所以有很多跨平台方案。

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

Built with Hugo
主题 StackJimmy 设计