LoginSignup
18

More than 5 years have passed since last update.

GO 1.5 と C++ を SWIG でブリッジさせる方法

Last updated at Posted at 2015-11-09

わかってしまえば簡単なのだが、引っかかることが多いのでメモ。
(検証環境:OSX El Capitan, GO 1.5.1, SWIG 3.0.7)


1 まず引数をエコーさせる単純な例を考える。

ソースディレクトリに以下の3ファイルを作る。

sc1.h
#pragma once 

inline  int
EchoIntN( int p ) {
    return p;
}

inline  double
EchoDoubleN( double p ) {
    return p;
}
sc1.swigcxx
%module sc1
%{
#include    "sc1.h"
%}

%include    "sc1.h"
nop.go
package sc1

以下を実行すると

go install

*. swigcxx があれば自動的に swig が呼び出され、pkg 化されてインストールされる。(GO 1.5 から)
ファイル nop.go は go ファイルが一つもないと go install が失敗してしまうので、その対策用のダミー。他に go ファイルがあれば必要ない。

呼び出し側は実行ディレクトリに以下のファイルを用意して

main1.go
package main

import  "fmt"
import  "github.com/Satachito/sc1"

func
Dump( p interface{} ) {
    fmt.Printf( "%v:%T\n", p, p )
}

func
main() {
    Dump( sc1.EchoIntN( 8 ) );
    Dump( sc1.EchoDoubleN( 9 ) );
}

実行

$ go run main1.go
8:int
9:float64


補足

もし nop.go ファイルを作るのが嫌だったら、sc1.swigcxx を別の名前(例えばsc1.i)に変えて、明示的に swig を呼び出してから go install すれば良い。

swig -go -c++ -cgo -intgosize 32 sc1.i
go install

sc1.go と sc1_wrap.cxx の2つのファイルが同一ディレクトリに作成される。


2 テンプレート化してみる。

sc2.h
#pragma once 

template    <typename T>    T
Echo( const T& p ) {
    return p;
}
sc2.swigcxx
%module sc2
%{
#include "sc2.h"
%}

%include "sc2.h"

%template(EchoIntT)     Echo<int>;
%template(EchoDoubleT)  Echo<double>;

最後の2行でテンプレートを実体化している。

nop.go
package sc2

として

go install

呼び出し側は

main2.go
package main

import  "fmt"
import  "github.com/Satachito/sc2"

func
Dump( p interface{} ) {
    fmt.Printf( "%v:%T\n", p, p )
}

func
main() {
    Dump( sc2.EchoIntT( 8 ) );
    Dump( sc2.EchoDoubleT( 9 ) );
}

$ go run main2.go
8:int
9:float64


3 string を使ってみる。

関数とテンプレート両方用意した。

sc3.h
#pragma once 

template    <typename T>    T
Echo( const T& p ) {
    return p;
}

#include    <string>

inline  std::string
EchoStringN( std::string p ) {
    return p;
}
sc3.swigcxx
%module sc3
%{
#include "sc3.h"
%}


%include    "std_string.i"

%include    "sc3.h"

%template(EchoStringT)  Echo<std::string>;

std_string.i を include するのがポイント

nop.go
package sc3

として

go install

呼び出し側は

main3.go
package main

import  "fmt"
import  "github.com/Satachito/sc3"

func
Dump( p interface{} ) {
    fmt.Printf( "%v:%T\n", p, p )
}

func
main() {
    Dump( sc3.EchoStringN( "Echo me N." ) );
    Dump( sc3.EchoStringT( "Echo me T." ) );
}

$ go run main3.go
Echo me N.:string
Echo me T.:string


4 vector を使ってみる。

sc4.h
#pragma once 

template    <typename T>    T
Echo( const T& p ) {
    return p;
}

#include    <string>
#include    <vector>

inline  std::vector<std::string>
EchoVSN( std::vector<std::string> p ) {
    return p;
}
sc4.swigcxx
%module sc4
%{
#include "sc4.h"
%}


%include    "std_string.i"
%include    "std_vector.i"

%include    "sc4.h"

namespace std {
    %template(StringVector) vector<string>;
}

%template(EchoVST)  Echo<std::vector<std::string>>;

std_vetor.i を include するのがポイント。

nop.go
package sc4

として

go install

呼び出し側は

main4.go
package main

import  "fmt"
import  "github.com/Satachito/sc4"

func
Dump( p interface{} ) {
    fmt.Printf( "%v:%T\n", p, p )
}

func
main() {
    a := sc4.NewStringVector()
    defer sc4.DeleteStringVector( a )
    a.Add( "Echo me." );
    Dump( sc4.EchoVSN( a ).Get( 0 ) );
    Dump( sc4.EchoVST( a ).Get( 0 ) );
}

$ go run main4.go
Echo me.:string
Echo me.:string

NewStringVector, DeleteStringVector, Add, Get は自動的に生成されている。他にもvectorとして扱うために必要なものが生成される。どんなメソッドが生成されているかを見るには、swig を明示的に呼び出して生成された go ファイルの中を見てみる。


5 struct を使ってみる。

sc5.h
#pragma once 

template    <typename T>    T
Echo( const T& p ) {
    return p;
}

struct
IF {
    int     I;
    float   F;
    double  Total() {
        return I + F;
    }
};

IF
EchoIFN( const IF& p ) {
    return p;
}
sc5.swigcxx
%module sc5
%{
#include "sc5.h"
%}

%include    "sc5.h"

%template(EchoIFT)  Echo<IF>;
nop.go
package sc5

として

go install

呼び出し側は

main5.go
package main

import  "fmt"
import  "github.com/Satachito/sc5"

func
Dump( p interface{} ) {
    fmt.Printf( "%v:%T\n", p, p )
}

func
main() {
    a := sc5.NewIF()
    defer sc5.DeleteIF( a )
    a.SetI( 1 )
    a.SetF( 2 )
    w1 := sc5.EchoIFN( a )
    Dump( w1.GetI() );
    Dump( w1.GetF() );
    Dump( w1.Total() );
    w2 := sc5.EchoIFT( a )
    Dump( w2.GetI() );
    Dump( w2.GetF() );
    Dump( w2.Total() );
}

$ go run main5.go
1:int
2:float32
3:float64
1:int
2:float32
3:float64

メンバーには直接アクセスできないので、setter, getter を使う。
忘れがちだけど、GO らしく C の構造体の方のメンバーの名前も最初大文字で。


Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18