わかってしまえば簡単なのだが、引っかかることが多いのでメモ。
(検証環境:OSX El Capitan, GO 1.5.1, SWIG 3.0.7)
#1 まず引数をエコーさせる単純な例を考える。
ソースディレクトリに以下の3ファイルを作る。
#pragma once
inline int
EchoIntN( int p ) {
return p;
}
inline double
EchoDoubleN( double p ) {
return p;
}
%module sc1
%{
#include "sc1.h"
%}
%include "sc1.h"
package sc1
以下を実行すると
go install
*. swigcxx があれば自動的に swig が呼び出され、pkg 化されてインストールされる。(GO 1.5 から)
ファイル nop.go は go ファイルが一つもないと go install が失敗してしまうので、その対策用のダミー。他に 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 テンプレート化してみる。
#pragma once
template <typename T> T
Echo( const T& p ) {
return p;
}
%module sc2
%{
#include "sc2.h"
%}
%include "sc2.h"
%template(EchoIntT) Echo<int>;
%template(EchoDoubleT) Echo<double>;
最後の2行でテンプレートを実体化している。
package sc2
として
go install
呼び出し側は
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 を使ってみる。
関数とテンプレート両方用意した。
#pragma once
template <typename T> T
Echo( const T& p ) {
return p;
}
#include <string>
inline std::string
EchoStringN( std::string p ) {
return p;
}
%module sc3
%{
#include "sc3.h"
%}
%include "std_string.i"
%include "sc3.h"
%template(EchoStringT) Echo<std::string>;
std_string.i を include するのがポイント
package sc3
として
go install
呼び出し側は
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 を使ってみる。
#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;
}
%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 するのがポイント。
package sc4
として
go install
呼び出し側は
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 を使ってみる。
#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;
}
%module sc5
%{
#include "sc5.h"
%}
%include "sc5.h"
%template(EchoIFT) Echo<IF>;
package sc5
として
go install
呼び出し側は
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 の構造体の方のメンバーの名前も最初大文字で。