Картирование примитивных типов данных из C
| Последнее обновление | 15 апреля 2019 г. |
В этом руководстве мы узнаем, какие типы данных C видны в Kotlin/Native и наоборот. Мы будем:
- Смотреть, какие типы данных есть в языке C
- Создавать небольшую библиотеку на C, которая использует эти типы в экспорте
- Просматривать сгенерированные Kotlin API из библиотеки 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++ содержится более подробная информация об этом.
Для каждой группы файлов .h мы будем использовать cinterop библиотеки C из 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 build.gradle.kts файл сборки Gradle со следующим содержимым:
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.3.21'
}
repositories {
mavenCentral()
}
kotlin {
macosX64("native") {
compilations.main.cinterops {
interop
}
binaries {
executable()
}
}
}
wrapper {
gradleVersion = "5.3.1"
distributionType = "ALL"
}
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.3.21'
}
repositories {
mavenCentral()
}
kotlin {
linuxX64("native") {
compilations.main.cinterops {
interop
}
binaries {
executable()
}
}
}
wrapper {
gradleVersion = "5.3.1"
distributionType = "ALL"
}
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.3.21'
}
repositories {
mavenCentral()
}
kotlin {
mingwX64("native") {
compilations.main.cinterops {
interop
}
binaries {
executable()
}
}
}
wrapper {
gradleVersion = "5.3.1"
distributionType = "ALL"
}
plugins {
kotlin("multiplatform") version "1.3.21"
}
repositories {
mavenCentral()
}
kotlin {
macosX64("native") {
val main by compilations.getting
val interop by main.cinterops.creating
binaries {
executable()
}
}
}
tasks.withType<Wrapper> {
gradleVersion = "5.3.1"
distributionType = Wrapper.DistributionType.ALL
}
plugins {
kotlin("multiplatform") version "1.3.21"
}
repositories {
mavenCentral()
}
kotlin {
linuxX64("native") {
val main by compilations.getting
val interop by main.cinterops.creating
binaries {
executable()
}
}
}
tasks.withType<Wrapper> {
gradleVersion = "5.3.1"
distributionType = Wrapper.DistributionType.ALL
}
plugins {
kotlin("multiplatform") version "1.3.21"
}
repositories {
mavenCentral()
}
kotlin {
mingwX64("native") {
val main by compilations.getting
val interop by main.cinterops.creating
binaries {
executable()
}
}
}
tasks.withType<Wrapper> {
gradleVersion = "5.3.1"
distributionType = Wrapper.DistributionType.ALL
}
Подготовленные исходные файлы проекта можно загрузить непосредственно с GitHub. GitHub. GitHub. GitHub. GitHub. GitHub.
Файл проекта настраивает работу с 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
./gradlew runDebugExecutableNative
gradlew.bat 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
- Сопоставление указателей на функции из C
- Сопоставление строк из C
Документация по взаимодействию с C охватывает более сложные сценарии взаимодействия.
© 2010–2020 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/tutorials/native/mapping-primitive-data-types-from-c.html