MoTLab -Mobility Technologies Engineering Blog-MoTLab -Mobility Technologies Engineering Blog-

SWIGでGoからC++を呼ぶ

行灯LaboGo低レイヤ
July 04, 2018

💁🏻
※本記事は Mobility Technologies の前身である JapanTaxi 時代に公開していたもので、記事中での会社やサービスに関する記述は公開当時のものです。
An image from Notion

社内のとあるプロジェクトで、C++で書かれたライブラリを使うことになりました。GoからCが使えることはよく知られています。こんな感じで。

// main.go
package main

import (
    // #include "increment.h"
    "C"
    "fmt"
)

func main() {
    i := 19
    fmt.Println(C.increment(C.int(i)))
}
// increment.h
int increment(int i);
// increment.c
#include "increment.h"

int increment(int i) {
    return i + 1;
}

実行すると「20」と表示されます。

C++もいけます。ただしextern “C”が必要です。

// increment.h
#ifdef __cplusplus
extern "C" {
#endif

int increment(int i);

#ifdef __cplusplus
}
#endif
// increment.cpp
#include "increment.h"

int increment(int i) {
    std::random_device rnd;
    return i + rnd();
}

ここでひとつ困ったことがあります。extern “C”しなければいけないということは、C++で書いた独自のクラスをGoから使うことはできないということです。

使いたいライブラリの関数やクラスのひとつひとつを、純粋なCのコードでラップしていくのは、ちょっと面倒です。

そんなときはSWIGを使ってみましょう。

こんなクラスがあるとします。

// point/point.h
class point {
public:
    int x;
    int y;
    void increment() {
        x++;
        y++;
    }
};

void increment(point *p);
// point/point.cpp
#include "point.h"

void increment(point *p) {
    p->x++;
    p->y++;
}

そしたらこんなファイルを置いて。

// point/point.i
%module point
%{
#include "point.h"
%}

%include "point.h"

おもむろにこう。

$ (cd point && swig -go -c++ -cgo -intgosize 64 point.i)

するとpoint/point.goとpoint/point_wrap.cxxが生成されて、下記のコードが動くようになります。

// main.go
package main

import (
    "sample/point"
    "fmt"
)

func main() {
    p := point.NewPoint()
    fmt.Println(p.GetX())
    point.Increment(p)
    fmt.Println(p.GetX())
    p.Increment()
    fmt.Println(p.GetX())
}

晴れてC++のクラスがGoから使えるようになりました。めでたしめでたし。

💁🏻
※本記事は Mobility Technologies の前身である JapanTaxi 時代に公開していたもので、記事中での会社やサービスに関する記述は公開当時のものです。

Mobility Technologies では共に日本のモビリティを進化させていくエンジニアを募集しています。話を聞いてみたいという方は、是非 募集ページ からご相談ください!