Cのプリミティブデータ型をマッピングする - チュートリアル
これは、KotlinとCのマッピングチュートリアルシリーズの最初のパートです。
Cのプリミティブデータ型をマッピングする
Cの構造体と共用体型をマッピングする
関数ポインタをマッピングする
Cの文字列をマッピングする
Cライブラリのインポートはベータ版です。cinteropツールによってCライブラリから生成されたすべてのKotlin宣言には、
@ExperimentalForeignApi
アノテーションが必要です。Kotlin/Nativeに同梱されているネイティブプラットフォームライブラリ(Foundation、UIKit、POSIXなど)は、一部のAPIでのみオプトインが必要です。
どのCデータ型がKotlin/Nativeで可視になるか(またその逆も)を探り、Kotlin/NativeとマルチプラットフォームGradleビルドにおける高度なC相互運用関連のユースケースを調べましょう。
このチュートリアルでは、以下のことを行います。
コマンドラインを使用してKotlinライブラリを生成できます。これは直接、またはスクリプトファイル(.sh
や.bat
ファイルなど)を使用しても可能です。 しかし、このアプローチは、何百ものファイルやライブラリを持つ大規模プロジェクトにはうまくスケールしません。 ビルドシステムを使用すると、Kotlin/Nativeコンパイラのバイナリやライブラリ、および推移的依存関係のダウンロードとキャッシュ、コンパイラとテストの実行を自動化することでプロセスが簡素化されます。 Kotlin/Nativeは、Kotlin Multiplatformプラグインを介してGradleビルドシステムを使用できます。
C言語の型
Cプログラミング言語には、以下のデータ型があります。
- 基本型:
char, int, float, double
と修飾子signed, unsigned, short, long
- 構造体、共用体、配列
- ポインタ
- 関数ポインタ
さらに、より具体的な型もあります。
C言語には、const
、volatile
、restrict
、atomic
という型修飾子もあります。
Cのデータ型がKotlinでどのように見えるかを見てみましょう。
Cライブラリを作成する
このチュートリアルでは、lib.c
ソースファイルは作成しません。これはCライブラリをコンパイルして実行する場合にのみ必要です。この設定では、cinteropツールの実行に必要な.h
ヘッダーファイルのみが必要です。
cinteropツールは、各.h
ファイルのセットに対してKotlin/Nativeライブラリ(.klib
ファイル)を生成します。生成されたライブラリは、Kotlin/NativeからCへの呼び出しを橋渡しするのに役立ちます。これには、.h
ファイルの定義に対応するKotlin宣言が含まれます。
Cライブラリを作成するには:
- 将来のプロジェクト用に空のフォルダを作成します。
- その中に、C関数がKotlinにどのようにマッピングされるかを確認するために、以下の内容の
lib.h
ファイルを作成します。
#ifndef LIB2_H_INCLUDED
#define LIB2_H_INCLUDED
void ints(char c, short d, int e, long f);
void uints(unsigned char c, unsigned short d, unsigned int e, unsigned long f);
void doubles(float a, double b);
#endif
このファイルにはextern "C"
ブロックがありませんが、この例では必要ありません。ただし、C++とオーバーロード関数を使用する場合は必要になる場合があります。詳細については、このStackoverflowスレッドを参照してください。
- 以下の内容で
lib.def
定義ファイルを作成します。
headers = lib.h
- cinteropツールによって生成されるコードにマクロやその他のC定義を含めることは役立つ場合があります。これにより、メソッド本体もコンパイルされ、バイナリに完全に含まれます。この機能を使用すると、Cコンパイラを必要とせずに実行可能な例を作成できます。
これを行うには、lib.h
ファイルからのC関数の実装を、---
区切り文字の後に新しいinterop.def
ファイルに追加します。
---
void ints(char c, short d, int e, long f) { }
void uints(unsigned char c, unsigned short d, unsigned int e, unsigned long f) { }
void doubles(float a, double b) { }
interop.def
ファイルは、アプリケーションをコンパイル、実行、またはIDEで開くために必要なすべてを提供します。
Kotlin/Nativeプロジェクトを作成する
詳細な最初のステップと新しいKotlin/Nativeプロジェクトを作成してIntelliJ IDEAで開く方法については、Kotlin/Nativeを始めるチュートリアルを参照してください。
プロジェクトファイルを作成するには:
プロジェクトフォルダに、以下の内容の
build.gradle(.kts)
Gradleビルドファイルを作成します。kotlinplugins { kotlin("multiplatform") version "2.2.10" } repositories { mavenCentral() } kotlin { macosArm64("native") { // macOS on Apple Silicon // macosX64("native") { // macOS on x86_64 platforms // linuxArm64("native") { // Linux on ARM64 platforms // linuxX64("native") { // Linux on x86_64 platforms // mingwX64("native") { // on Windows val main by compilations.getting val interop by main.cinterops.creating binaries { executable() } } } tasks.wrapper { gradleVersion = "8.14" distributionType = Wrapper.DistributionType.BIN }
groovyplugins { id 'org.jetbrains.kotlin.multiplatform' version '2.2.10' } repositories { mavenCentral() } kotlin { macosArm64("native") { // Apple Silicon macOS // macosX64("native") { // macOS on x86_64 platforms // linuxArm64("native") { // Linux on ARM64 platforms // linuxX64("native") { // Linux on x86_64 platforms // mingwX64("native") { // Windows compilations.main.cinterops { interop } binaries { executable() } } } wrapper { gradleVersion = '8.14' distributionType = 'BIN' }
このプロジェクトファイルは、C相互運用をビルドステップとして追加で設定します。 設定のさまざまな方法については、Multiplatform Gradle DSL リファレンスを参照してください。
interop.def
、lib.h
、lib.def
ファイルをsrc/nativeInterop/cinterop
ディレクトリに移動します。src/nativeMain/kotlin
ディレクトリを作成します。Gradleの規約に従い、ここにすべてのソースファイルを配置します。
デフォルトでは、Cからのすべてのシンボルはinterop
パッケージにインポートされます。
src/nativeMain/kotlin
に、以下の内容のhello.kt
スタブファイルを作成します。kotlinimport interop.* import kotlinx.cinterop.ExperimentalForeignApi @OptIn(ExperimentalForeignApi::class) fun main() { println("Hello Kotlin/Native!") ints(/* fix me*/) uints(/* fix me*/) doubles(/* fix me*/) }
Cのプリミティブ型宣言がKotlin側からどのように見えるかを確認した後で、コードを完成させます。
Cライブラリの生成されたKotlin APIを調査する
Cのプリミティブ型がKotlin/Nativeにどのようにマッピングされるかを見て、それに応じてサンプルプロジェクトを更新しましょう。
IntelliJ IDEAのGo to declarationコマンド(/)を使用して、C関数用に生成された以下のAPIに移動します。
fun ints(c: kotlin.Byte, d: kotlin.Short, e: kotlin.Int, f: kotlin.Long)
fun uints(c: kotlin.UByte, d: kotlin.UShort, e: kotlin.UInt, f: kotlin.ULong)
fun doubles(a: kotlin.Float, b: kotlin.Double)
Cの型は直接マッピングされますが、char
型は通常8ビットの符号付き値であるためkotlin.Byte
にマッピングされます。
C | Kotlin |
---|---|
char | kotlin.Byte |
unsigned char | kotlin.UByte |
short | kotlin.Short |
unsigned short | kotlin.UShort |
int | kotlin.Int |
unsigned int | kotlin.UInt |
long long | kotlin.Long |
unsigned long long | kotlin.ULong |
float | kotlin.Float |
double | kotlin.Double |
Kotlinコードを更新する
Cの定義を確認したところで、Kotlinコードを更新できます。hello.kt
ファイルの最終コードは次のようになります。
import interop.*
import kotlinx.cinterop.ExperimentalForeignApi
@OptIn(ExperimentalForeignApi::class)
fun main() {
println("Hello Kotlin/Native!")
ints(1, 2, 3, 4)
uints(5u, 6u, 7u, 8u)
doubles(9.0f, 10.0)
}
すべてが期待通りに動作することを確認するには、IDEでrunDebugExecutableNative
Gradleタスクを実行するか、以下のコマンドを使用してコードを実行します。
./gradlew runDebugExecutableNative
次のステップ
シリーズの次のパートでは、構造体と共用体型がKotlinとCの間でどのようにマッピングされるかについて学びます。
関連項目
より高度なシナリオを扱うCとの相互運用ドキュメントで詳細を学びましょう。