! C++とSwift間で構造体を渡すことは保証されておらず、メモリレイアウトが異なる場合があるそうです。
この記事は参考程度に
背景
Swiftは比較的高速な言語ですが、やっぱり速度面でC++を使いたくなることはあります。
しかし、Swiftが直接サポートしているのはCからのブリッジのみです。なので、
C++の構造体をObjCでラップして...
なんて形で渡すことが多いです。
でもこれだとかなりオーバヘッドが大きくて(特にObjCの動的ディスパッチあたりが)、結局C++を使った意味は...?となることが多いです。
なので、実はSwiftの構造体くらいならC++に直接渡せるよって話です。
実験
Swiftでこんな構造体を作ります。
struct Point {
var x: Double
var y: Double
}
struct Size {
var width: Double
var height: Double
}
struct Rect {
var origin: Point
var size: Size
}
それをポインタにして、cppcall
に投げます。
var frame = Rect(origin: Point(x: 100, y: 200), size: Size(width: 300, height: 400))
withUnsafeMutablePointer(to: &frame) {ptr in
cppcall(UnsafeMutableRawPointer(ptr))
}
次にC++側です。
Swiftで定義した構造体と同じ構造を持つ構造体をC++側で定義します。
struct Point {
double x;
double y;
};
struct Size {
double width;
double height;
};
struct Rect {
Point origin;
Size size;
};
void cppcall(void *swiftStruct) {
auto casted = (struct Rect*)(swiftStruct);
std::cout << casted->origin.x << std::endl;
std::cout << casted->size.height << std::endl;
}
Headerで以下のようにextern "C"します。受け取るのは void*
型です。
extern "C" {
void cppcall(void* swiftStruct);
}
これで、実行するとそのままC++にSwiftでインスタンス化した構造体がC++で使えます。
解説のようなもの
これがうまく行くのはSwiftとC++でメモリ上の構造体の扱いが同じだからです。どちらもメンバーを最初から順番にメモリに並べているだけです。(SwiftはOptionalな構造体ではちょっと扱いが変わりますが...)