Сопоставление примитивных типов данных из C – учебник
В этом учебнике вы узнаете, какие типы данных C видны в Kotlin/Native и наоборот. Вы:
Увидите, какие типы данных используются в языке C.
Создадите небольшую библиотеку на C, которая использует эти типы в экспорте.
Узнаете, как примитивные типы в Kotlin сопоставляются с C.
Типы данных в языке C
Какие типы существуют в языке C? Давайте возьмём статью типов данных C из Википедии в качестве основы. В языке программирования C есть следующие типы:
базовые типы
char, int, float, doubleс модификаторамиsigned, unsigned, short, longструктуры, объединения, массивы
указатели
указатели на функции
Также существуют более специфические типы:
логический тип (из C99)
size_tиptrdiff_t(такжеssize_t)целые типы с фиксированной шириной, такие как
int32_tилиuint64_t(из C99)
Также в языке C существуют следующие квалификаторы типов: const, volatile, restruct, atomic
Лучший способ увидеть, какие типы данных C видны в Kotlin, — это попробовать.
Пример библиотеки на C
Создайте файл lib.h, чтобы увидеть, как функции C сопоставляются с 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++ и перегруженные функции. В теме совместимости с C++ на Stackoverflow содержатся более подробные сведения об этом.
Для каждой группы файлов .h, вы будете использовать инструмент cinterop из Kotlin/Native для генерации Kotlin/Native библиотеки или .klib. Сгенерированная библиотека обеспечит мостик для вызовов из Kotlin/Native в C. Она включает соответствующие Kotlin объявления для определений из файлов .h. Для запуска инструмента cinterop достаточно иметь файл .h. И вам не нужно создавать файл lib.c, если вы не хотите скомпилировать и запустить пример. Более подробная информация об этом содержится на странице взаимодействия с C. Для учебника достаточно создать файл lib.def со следующим содержимым:
headers = lib.h
Вы можете включить все объявления непосредственно в файл .def после разделителя ---. Это может быть полезно для включения макросов или других определений C в код, генерируемый инструментом cinterop. Тела методов также компилируются и полностью включаются в двоичный файл. Используйте эту функцию для создания работающего примера без необходимости в компиляторе C. Для этого необходимо добавить реализации функций C из файла lib.h и поместить эти функции в файл .def. Вы получите следующий 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. Теперь пришло время создать файлы проекта, открыть проект в IntelliJ IDEA и запустить его.
Проверка сгенерированных Kotlin API для библиотеки на C
Хотя можно использовать командную строку, как непосредственно, так и в сочетании со скриптовым файлом (например, .sh или .bat файл), этот подход не масштабируется для крупных проектов с сотнями файлов и библиотек. В таком случае лучше использовать компилятор Kotlin/Native с системой сборки, поскольку это помогает загружать и кешировать двоичные файлы и библиотеки компилятора Kotlin/Native с транзитивными зависимостями и запускать компилятор и тесты. Kotlin/Native может использовать систему сборки Gradle через плагин kotlin-multiplatform.
Мы рассмотрели основы настройки IDE-совместимого проекта с Gradle в учебнике Простой Kotlin/Native Приложение. Проверьте его, если вам нужны подробные начальные шаги и инструкции о том, как начать новый Kotlin/Native проект и открыть его в IntelliJ IDEA. В этом учебнике мы рассмотрим расширенные использования Kotlin/Native, связанные с взаимодействием с C, и многоплатформенные сборки с Gradle.
Сначала создайте папку проекта. Все пути в этом руководстве будут относительными к этой папке. Иногда необходимо создать отсутствующие директории перед добавлением новых файлов.
Используйте следующий build.gradle(.kts) файл сборки Gradle:
plugins {
kotlin("multiplatform") version "1.8.0"
}
repositories {
mavenCentral()
}
kotlin {
linuxX64("native") { // on Linux
// macosX64("native") { // on x86_64 macOS
// macosArm64("native") { // on Apple Silicon macOS
// mingwX64("native") { // on Windows
val main by compilations.getting
val interop by main.cinterops.creating
binaries {
executable()
}
}
}
tasks.wrapper {
gradleVersion = "7.3"
distributionType = Wrapper.DistributionType.BIN
}
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.8.0'
}
repositories {
mavenCentral()
}
kotlin {
linuxX64('native') { // on Linux
// macosX64("native") { // on x86_64 macOS
// macosArm64("native") { // on Apple Silicon macOS
// mingwX64('native') { // on Windows
compilations.main.cinterops {
interop
}
binaries {
executable()
}
}
}
wrapper {
gradleVersion = '7.3'
distributionType = 'BIN'
}
Файл проекта настраивает взаимодействие с C как дополнительный шаг сборки. Переместите файл interop.def в директорию src/nativeInterop/cinterop. Gradle рекомендует использовать соглашения вместо конфигураций, например, ожидается, что исходные файлы будут находиться в папке src/nativeMain/kotlin. По умолчанию все символы из C импортируются в пакет interop, вы можете импортировать весь пакет в наши файлы .kt. Обратитесь к документации плагина kotlin-multiplatform, чтобы узнать о всех различных способах его настройки.
Создайте файл-заглушку src/nativeMain/kotlin/hello.kt со следующим содержимым, чтобы увидеть, как объявления примитивных типов C видны из Kotlin:
import interop.*
fun main() {
println("Hello Kotlin/Native!")
ints(/* fix me*/)
uints(/* fix me*/)
doubles(/* fix me*/)
}
Теперь вы готовы открыть проект в IntelliJ IDEA и посмотреть, как исправить пример проекта. При этом посмотрите, как примитивные типы C сопоставляются с Kotlin/Native.
Примитивные типы в Kotlin
С помощью IntelliJ IDEA (Перейти | Объявление) или ошибок компилятора, вы видите следующую сгенерированную API для функций C:
fun ints(c: Byte, d: Short, e: Int, f: Long) fun uints(c: UByte, d: UShort, e: UInt, f: ULong) fun doubles(a: Float, b: 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 |
Исправление кода
Вы видели все определения, и сейчас пришло время исправить код. Запустите задачу runDebugExecutableNative Gradle в IDE или используйте следующую команду для запуска кода:
./gradlew runDebugExecutableNative
Конечный код в файле hello.kt может выглядеть так:
import interop.*
fun main() {
println("Hello Kotlin/Native!")
ints(1, 2, 3, 4)
uints(5, 6, 7, 8)
doubles(9.0f, 10.0)
}
Следующие шаги
Продолжайте изучать более сложные типы языка C и их представление в Kotlin/Native в следующих учебниках:
Документация по взаимодействию с C охватывает более сложные сценарии взаимодействия.
© 2010–2023 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/mapping-primitive-data-types-from-c.html