Cのプリミティブデータ型のマッピング – チュートリアル
これは、KotlinとCのマッピングチュートリアルシリーズの最初の部分です。
Cからのプリミティブデータ型のマッピング
Cからの構造体と共用体のマッピング
関数ポインタのマッピング
Cからの文字列のマッピング
CライブラリのインポートはExperimentalです。cinteropツールによってCライブラリから生成されたすべてのKotlinの宣言には、@ExperimentalForeignApi
アノテーションが必要です。
Kotlin/Nativeに同梱されているネイティブプラットフォームライブラリ(Foundation、UIKit、POSIXなど)は、一部のAPIのみオプトインが必要です。
Kotlin/NativeでどのCデータ型が表示されるか、またその逆も調べ、Kotlin/NativeとマルチプラットフォームGradleビルドのC interop関連の高度なユースケースを検証しましょう。
このチュートリアルでは、次のことを行います。
コマンドラインを使用してKotlinライブラリを生成できます。直接、またはスクリプトファイル(.sh
や.bat
ファイルなど)を使用します。
ただし、このアプローチは、数百のファイルとライブラリを持つ大規模なプロジェクトにはうまくスケールしません。
ビルドシステムを使用すると、Kotlin/Nativeコンパイラのバイナリとライブラリを推移的な依存関係とともにダウンロードしてキャッシュし、コンパイラとテストを実行することで、プロセスが簡素化されます。
Kotlin/Nativeは、Kotlin Multiplatform pluginを介してGradleビルドシステムを使用できます。
C言語の型
Cプログラミング言語には、次のデータ型があります。
- 基本型:
char, int, float, double
に修飾子signed, unsigned, short, long
- 構造体、共用体、配列
- ポインタ
- 関数ポインタ
より具体的な型もあります。
C言語には、次の型修飾子もあります:const
, volatile
, restrict
, atomic
。
KotlinでどのCデータ型が表示されるかを見てみましょう。
Cライブラリを作成する
このチュートリアルでは、Cライブラリをコンパイルして実行する場合にのみ必要なlib.c
ソースファイルは作成しません。このセットアップでは、cinterop toolの実行に必要な.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 threadを参照してください。 -
次の内容で
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ビルドファイルを作成します。- Kotlin
- Groovy
plugins {
kotlin("multiplatform") version "2.1.20"
}
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.10"
distributionType = Wrapper.DistributionType.BIN
}plugins {
id 'org.jetbrains.kotlin.multiplatform' version '2.1.20'
}
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.10'
distributionType = 'BIN'
}プロジェクトファイルは、C interopを追加のビルドステップとして構成します。 さまざまな構成方法については、Multiplatform Gradle DSL referenceを参照してください。
-
interop.def
、lib.h
、およびlib.def
ファイルをsrc/nativeInterop/cinterop
ディレクトリに移動します。 -
src/nativeMain/kotlin
ディレクトリを作成します。これは、構成の代わりに規約を使用するというGradleの推奨事項に従って、すべてのソースファイルを配置する必要がある場所です。デフォルトでは、Cからのすべてのシンボルが
interop
パッケージにインポートされます。 -
src/nativeMain/kotlin
に、次の内容でhello.kt
スタブファイルを作成します。import 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コマンド(
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)
char
型を除いて、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の間でどのようにマッピングされるかを学びます。
参照
より高度なシナリオをカバーするInteroperability with Cドキュメントで詳細をご覧ください。