SwiftからC言語の関数をコールしてみました。
SwiftからC言語の関数をコールした場合の引数の渡し方について調べてみました。
XCode 10.1で確認しました。
とりあえず、分かったことはSwiftで&はアドレス演算子ではないです。
うまいこと型類推してくれる場合もあったりしますが、自作の構造体の場合はきちんと型変換しないとダメです。
1 引数なし、戻り値なし
void func1(void)
{
return;
}
func1()
簡単ですね。
2 引数 int、戻り値なし
void func2(int a, int b)
{
return;
}
func2(1,2)
これも、簡単ですね。
3 引数 int、戻り値 int
int func3(int a, int b)
{
return a+b;
}
// printで出力
print("func3()", func3(1,2))
// 変数に代入
let c = func3(7,8)
print("func3()", c)
これも、簡単ですね。
4 引数 charの配列のポインタ
void func4(const char *in, char *out)
{
unsigned long len = strlen(in) + 1;
out[0] = 'A';
memcpy(&out[1], in, len);
return;
}
var outstr : [CChar] = [0, 0, 0, 0, 0, 0]
func4("1234", &outstr)
print("func4()", outstr)
print("func4()", String(cString : outstr))
引数がconst char *
の場合はStringなリテラルを渡すとCの文字列に変換してくれます。
引数がchar *
の場合は変数のポインタを渡さないとダメです。
func4() [65, 49, 50, 51, 52, 0]
func4() A1234
5 引数 構造体
struct location {
int x;
int y;
};
int func5(struct location loc);
int func5(struct location loc)
{
loc.x += 1;
loc.y += 1;
return loc.x + loc.y;
}
var l = location.init(x:1,y:2)
print("fnc5()", func5(l))
構造体の初期化にinit()が使えます。
6 引数 構造体のポインタ
struct location {
int x;
int y;
};
int func6(struct location *loc);
int func5(struct location loc)
{
loc.x += 1;
loc.y += 1;
return loc.x + loc.y;
}
var l = location.init(x:1,y:2)
withUnsafeMutablePointer(to: &l){
let p = $0
print("func6() before", p.pointee)
func6(p)
print("func6() after", p.pointee)
}
構造体のポインタを取得するにはwithUnsafeMutablePointer()を使います。pの型はUnsafeMutablePointerになります。
構造体のポインタを参照する場合はp.pointeeを使います。
構造体のポインタはwithUnsafeMutablePointer()のbody中のみ有効です。
7 Data型をポインタとして渡して、出力されたポインタをData型にする。
Cで書かれたエンコーダ関数があるとします。
uint32_t hogeEnc(void *inp, void *outp);
こんな感じで、無駄なコピーが発生していますが、動きました。
func encode(data:Data) -> Data {
let input = UnsafeMutablePointer<UInt8>.allocate(capacity: 1920)
let output = UnsafeMutablePointer<UInt8>.allocate(capacity: 1920)
data.copyBytes(to: input, count: data.count)
let enclen = hogeEnc(input, output)
let encdata = Data(buffer: UnsafeMutableBufferPointer(start: output, count: Int(enclen)))
input.deinitialize(count: 1920)
output.deinitialize(count: 1920)
return encdata
}
まず、入力用と出力用のポインタを生成し領域を確保します。
引数のData型のバイト列をinputポインタが指す領域にコピーします。
Cのエンコード関数を呼びます。
エンコード関数でエンコードした結果をData型に変換します。その際にUnsafeMutablePointer型をUnsafeMutableBufferPointer型に変換してます。
最後にポインタを解放します。(必要なのかは不明ですが念の為です。)
C言語で確保した領域外へアクセスしないように注意しましょう。
とりあえず以上です。