映射来自 C 语言的原生数据类型——教程
This is the first part of the Mapping Kotlin and C tutorial series.
Mapping primitive data types from C
Mapping struct and union types from C
Mapping function pointers
Mapping strings from C
The C libraries import is Experimental. All Kotlin declarations generated by the cinterop tool from C libraries should have the
@ExperimentalForeignApi
annotation.Native platform libraries shipped with Kotlin/Native (like Foundation, UIKit, and POSIX) require opt-in only for some APIs.
{style="warning"}
Let's explore which C data types are visible in Kotlin/Native and vice versa and examine advanced C interop-related use cases of Kotlin/Native and multiplatform Gradle builds.
在本教程中会:
You can use the command line to generate a Kotlin library, either directly or with a script file (such as .sh
or .bat
file).
However, this approach doesn't scale well for larger projects that have hundreds of files and libraries.
Using a build system simplifies the process by downloading and caching the Kotlin/Native
compiler binaries and libraries with transitive dependencies, as well as by running the compiler and tests.
Kotlin/Native can use the Gradle build system through the Kotlin Multiplatform plugin.
C 语言中的类型
C 编程语言有以下数据类型:
- 基本类型:
char、int、float、double
以及带修饰符的signed、unsigned、short、long
- 结构体、联合体、数组
- 指针
- 函数指针
还有一些更多的具体类型:
C 语言中还有以下类型限定符:const
、volatile
、restrict
、atomic
。
Let's see which C data types are visible in Kotlin.
Create a C library
In this tutorial, you won't create a lib.c
source file, which is only necessary if you want to
compile and run your C library. For this setup, you'll only need a .h
header file that is required for running
the cinterop tool.
The cinterop tool generates a Kotlin/Native library (a .klib
file) for each set of .h
files. The generated library
helps bridge calls from Kotlin/Native to C. It includes Kotlin declarations that correspond to the definitions from the
.h
files.
To create a C library:
- Create an empty folder for your future project.
Inside, create a
lib.h
file with the following content to see how C functions are mapped into Kotlin:#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 这贴了解更多细节。Create the
lib.def
definition file with the following content:headers = lib.h
将宏或其他 C 定义包含在 cinterop 工具生成的代码中会很有帮助。这样, 方法体同样被编译以及完全包含到二进制文件中。使用这个特性,可以创建一个无需 C 编译器的可运行示例。
为做到这点,在新
interop.def
文件中---
分隔符之后,为lib.h
文件中的 C 函数添加实现:--- 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 中编译、运行或打开应用程序所需的一切。
Create a Kotlin/Native project
See the Get started with Kotlin/Native tutorial for detailed first steps and instructions on how to create a new Kotlin/Native project and open it in IntelliJ IDEA.
{style="tip"}
To create project files:
- In your project folder, create a
build.gradle(.kts)
Gradle build file with the following content:
【Kotlin】
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
}
【Groovy】
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'
}
The project file configures the C interop as an additional build step. Check out the Multiplatform Gradle DSL reference to learn about different ways you can configure it.
- Move your
interop.def
,lib.h
, andlib.def
files to thesrc/nativeInterop/cinterop
directory. Create a
src/nativeMain/kotlin
directory. This is where you should place all the source files, following Gradle's recommendations on using conventions instead of configurations.By default, all the symbols from C are imported to the
interop
package.在
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*/) }
You'll complete the code later as you learn how C primitive type declarations look from the Kotlin side.
Inspect generated Kotlin APIs for a C library
Let's see how C primitive types are mapped into Kotlin/Native and update the example project accordingly.
Use IntelliJ IDEA's Go to declaration
command (
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
类型,它映射到了 kotlin.Byte
,因为它通常是 8 位有符号值:
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 |
Update Kotlin code
Now that you've seen the C definitions, you can update your Kotlin code. 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)
}
To verify that everything works as expected, run the runDebugExecutableNative
Gradle task in your IDE
or use the following command to run the code:
./gradlew runDebugExecutableNative
接下来
In the next part of the series, you'll learn how struct and union types are mapped between Kotlin and C:
See also
Learn more in the Interoperability with C documentation that covers more advanced scenarios.