LoginSignup
11
15

More than 3 years have passed since last update.

SwiftからC言語の関数をコール

Last updated at Posted at 2019-02-24

SwiftからC言語の関数をコールしてみました。

SwiftからC言語の関数をコールした場合の引数の渡し方について調べてみました。
XCode 10.1で確認しました。

とりあえず、分かったことはSwiftで&はアドレス演算子ではないです。
うまいこと型類推してくれる場合もあったりしますが、自作の構造体の場合はきちんと型変換しないとダメです。

1 引数なし、戻り値なし

c
void func1(void)
{
    return;
}
swift
func1()

簡単ですね。

2 引数 int、戻り値なし

c
void func2(int a, int b)
{
    return;
}
swift
func2(1,2)

これも、簡単ですね。

3 引数 int、戻り値 int

c
int func3(int a, int b)
{
    return a+b;
}
swift
// printで出力
print("func3()", func3(1,2))
// 変数に代入
let c = func3(7,8)
print("func3()", c)

これも、簡単ですね。

4 引数 charの配列のポインタ

c
void func4(const char *in, char *out)
{
    unsigned long len = strlen(in) + 1;
    out[0] = 'A';
    memcpy(&out[1], in, len);
    return;
}
swift
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 引数 構造体

c
struct location {
    int x;
    int y;
};

int func5(struct location loc);
c
int func5(struct location loc)
{
    loc.x += 1;
    loc.y += 1;
    return loc.x + loc.y;
}
swift
var l = location.init(x:1,y:2)
print("fnc5()", func5(l))

構造体の初期化にinit()が使えます。

6 引数 構造体のポインタ

h
struct location {
    int x;
    int y;
};

int func6(struct location *loc);
c
int func5(struct location loc)
{
    loc.x += 1;
    loc.y += 1;
    return loc.x + loc.y;
}
swift
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言語で確保した領域外へアクセスしないように注意しましょう。

とりあえず以上です。

11
15
1

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
11
15