SWIGでGoからC++を呼ぶ

開発部 大崎
去年までRailsを書いてました。今年はGoを書いてます。

社内のとあるプロジェクトで、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から使えるようになりました。めでたしめでたし。

JapanTaxiに興味を持ったら、まずはお話しませんか?