SwiftからC言語の関数を使う

SwiftからC言語の関数を直接使うためには、Swiftでメモリバッファを考慮したコードを書く必要があります。Unsafe系のタイプを使う必要があり、分かりづらいコードが多くなります。

この記事ではコード例を紹介します。

String から Cスタイル文字列を渡す

まずは、HelloWorld の例からです。但し、Swift の print 関数ではなく、 C の printf を使っています。

次のようなCのコードがあるとします。引数で渡されたC文字列をprintfで出力します。SwiftはCの可変長引数関数には対応していないので、sayというラッパー関数を作りました。

void say(const char *str)
{
    printf("%s\n", str);
}

Swift側のコードは次の通りです。ポイントはSwiftのStringからC文字列を取得するために、cString(using:) 関数を使っています。

import Foundation

let str = "Hello, World!"
say(str.cString(using: .utf8))

C側で文字列を返す

逆に、C側で文字列を書き込めるように、Swiftからはバッファを渡す場合です。

C側の関数は次のように渡されたバッファに文字列を書き込む関数があるとします。

void expression(int i, int j, char *result)
{
    int k = i + j;
    sprintf(result, "%d + %d = %d", i, j, k);
}

Swift側のコードは次のようになります。

var buf = [CChar](repeating: 0, count: 256)
expression(1, 2, &buf)
if let str = String(cString: buf, encoding: .utf8) {
    print(str)
}

Cのintには、SwiftのIntを直接渡すことができます。char *のバッファは、CCharの配列を作り、「&」演算子を使います。「&」演算子を使うことで UnsafeMutablePointer<CChar> を取得できます。bufには、Cスタイルの文字列が書き込まれているので、String(cString:encoding:)イニシャライザを使って、Stringを作ります。返されるStringはオプショナルなので、if let で通常の変数に変換しています。

スカラーのポインタ渡し

スカラーのポインタを渡すときは「&」演算子を使います。

次のようなCのコードがあるとします。

void say2(const int *ip)
{
    printf("%d\n", *ip);
}

Swift側のコードは次のようになります。

var i: Int32 = 10
say2(&i)

C側で値を書き込むときも同じです。次のようなCのコードがあるとします。

void calc(int i, int j, int *result)
{
    *result = i * j;
}

Swift側のコードは次のようになります。

var result: Int32 = 0
calc(10, 20, &result)
print(result)

構造体のポインタ渡し

C側で定義された構造体はSwiftの構造体に自動的にマッピングされます。

Cの関数は引数に構造体へのポインタを渡すケースが多々あります。

次のようなCのコードがあるとします。

typedef struct POINT {
    double x;
    double y;
} POINT;

void movePoint(POINT *point, double dx, double dy)
{
    point->x += dx;
    point->y += dy;
}

この関数を利用するSwift側のコードは次のようになります。

var pt = POINT(x: 0, y: 0)
movePoint(&pt, -10, 20)

print("x=\(pt.x), y=\(pt.y)")

配列を渡す

C側の関数が配列を引数に取るときのコード例です。次のようなCのコードがあるとします。

void printArray(const int *array, int count)
{
    for (int i = 0; i < count; i++)
    {
        printf("[%d]: %d\n", i, array[i]);
    }
}

Swift側のコードは次のようになります。スカラーや構造体と同じように、「&」演算子を使用します。

var values: [Int32] = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
printArray(&values, Int32(values.count))

C側で書き込むときも同じです。例えば次のようなCの関数があるとします。

void fillArray(int *array, int count)
{
    for (int i = 0; i < count; i++)
    {
        array[i] = i;
    }
}

これを呼び出すSwift側のコードは次のようになります。Swift側で値を0にした配列を作って渡します。

var values = [Int32](repeating: 0, count: 10)
fillArray(&values, Int32(values.count))

print(values)

ヒープを直に使う

mallocで確保したバッファを使う必要があるときは、Swiftからもmallocを使うことができます。例えば、次のようなCのコードがあるとします。

typedef struct POINT {
    double x;
    double y;
} POINT;

typedef struct SHAPE {
    POINT   *pointList;
    int     numOfPoints;
} SHAPE;

void printShape(const SHAPE *shape)
{
    printf("[");
    for (int i = 0; i < shape->numOfPoints; i++)
    {
        if (i + 1 < shape->numOfPoints)
        {
            printf("(%g,%g), ", shape->pointList[i].x, shape->pointList[i].y);
        }
        else
        {
            printf("(%g,%g)", shape->pointList[i].x, shape->pointList[i].y);
        }
    }
    printf("]\n");
}

void freeShape(SHAPE *shape)
{
    free(shape->pointList);
}

freeShape関数はfree関数でバッファを解放するので、malloc関数でバッファを確保する必要があります。Swift側のコードは次のようになります。

var shape = SHAPE()

var buf = malloc(MemoryLayout<POINT>.size * 3)
if buf != nil {
    if let pointList = buf?.bindMemory(to: POINT.self, capacity: 3) {
        let points: [POINT] = [POINT(x: 10, y: 1),
                               POINT(x: 20, y: 2),
                               POINT(x: 30, y: 3)]

        for (i, pt) in points.enumerated() {
            pointList[i] = pt
        }
        
        shape.pointList = pointList
        shape.numOfPoints = 3
    }
}

printShape(&shape)
freeShape(&shape)

ポイントはmalloc関数を使うことですが、そのためにはSHAPE構造体のバイト長を調べる必要があります。Cではsizeof()演算子を使用しますが、Swiftの場合はこのようにMemoryLayoutを使用します。また、返されたバッファはUnsafeMutableRawPointerなので、bindを使って、UnsafeMutableBuffer<POINT>に変換しています。

Cの構造体の配列メンバーへの書き込み

Cの構造体の配列メンバーは、Swiftのタプルにマッピングされます。タプルは一つのタイプになってしまうので扱いが厄介です。Swift側から書き込むためには、タプルのポインタを使います。

次のようなCのコードがあるとします。

typedef struct SHAPE {
    char    name[256];
    POINT   *pointList;
    int     numOfPoints;
} SHAPE;

void printShapeName(const SHAPE *shape)
{
    printf("%s\n", shape->name);
}

このSHAPE構造体のnameはSwiftでは、256個のInt8で構成されるタプルになります。ここにCの文字列を書き込むSwiftのコードは次のようになります。

func tupleMutablePointer(p: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer {
    return p
}

var shape = SHAPE()
let namePtr = tupleMutablePointer(p: &shape.name).bindMemory(to: Int8.self, capacity: 256)
strcpy(namePtr, "Triangle".cString(using: .utf8))

printShapeName(&shape)

tupleMutablePointerは単純に引数で渡されたポインターを返すだけですが、これを入れることで、タプルのポインターを取得できます。

返されたUnsafeMutableRawPointerからUnsafeMutablePointer<Int8>への変換は、bindMemory(to:capacity:)を使用します。

 

投稿者プロフィール

林 晃
林 晃macOS/iOSアプリ/SDK/ミドルウェア開発が専門の開発者
アールケー開発代表。macOS/iOSアプリ/SDK/ミドルウェア開発が専門の開発者。ObjC/Swift/C++使い。豊富な開発実務経験を基に、教育コンテンツ開発、技術書執筆、技術指導、技術セミナー講師、企業内研修講師、行政・自治体職員研修講師も行います。

基礎から学ぶMetal


「基礎から学ぶMetal」を執筆しました。本書はMetalを使ってGPUプログラミングを行うための最初のステップを解説するMetalの解説書です。

私が初めてGPUプログラミングを行ったとき、どこから手をつけて、学んでいけば良いのか分からず呆然としました。もし、あのとき、これを教えてくれればという部分を解説しました。本書で解説している部分はMetalの基礎となる部分で、Metalを使うときに必ず触れることになる部分です。

詳細

基礎から学ぶSwiftUI


「基礎から学ぶ SwiftUI」というタイトルの本を執筆しました。

SwiftUIの入門書です。

SwiftUIのコンセプトは「ユーザーインターフェイスを作るための最短パスを提供する」「一度学べば(Appleのプラットフォームの)どこにでも適用できる」です。

SwiftUIの概要から始まって、一つ一つのテクノロジートピックに注目しながらSwiftUIとは何か?どんなことができるのか?どのようなコードを書けば良いのかなどを丸々一冊使って解説しています。

詳細

関連記事

  1. コマンドライン引数でモックに差し替える

  2. M1 Mac への JDK のセットアップ

  3. M1 Mac でのドライバの読み込み判断フロー

  4. 【Python】ArgumentParserでコマンドライン引数を取得…

  5. Cのバイナリデータを確保する処理をSwiftで書く

  6. Apple Notarization Service に対応するための…

最近の著書

  1. 基礎から学ぶ SwiftUI

人気記事

最近の記事