Преобразование примитивных типов данных из C – учебник
В этом учебнике вы узнаете, какие типы данных C видны в Kotlin/Native и наоборот. Вы будете:
Узнавать Типы данных в языке C.
Создавать небольшую библиотеку на C, которая использует эти типы в экспортах.
Просматривать сгенерированные Kotlin API из библиотеки на C.
Выяснять, как Примитивные типы в Kotlin отображаются в C.
Типы в языке C
Какие типы существуют в языке C? Давайте возьмем статью типов данных C из Википедии в качестве основы. В языке программирования C существуют следующие типы:
базовые типы
char, int, float, doubleс модификаторамиsigned, unsigned, short, longструктуры, объединения, массивы
указатели
указатели на функции
Также есть более специфические типы:
тип boolean (из 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. Достаточно иметь только файл .h, чтобы запустить инструмент cinterop. И вам не нужно создавать файл 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, и сборки multiplatform с Gradle.
Сначала создайте папку проекта. Все пути в этом учебнике будут относительными к этой папке. Иногда недостающие каталоги необходимо создать перед добавлением новых файлов.
Используйте следующий файл сборки Gradle build.gradle(.kts):
plugins {
kotlin("multiplatform") version "1.6.20"
}
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 = "6.7.1"
distributionType = Wrapper.DistributionType.BIN
}
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.6.20'
}
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 = '6.7.1'
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's Перейти к | Объявление или ошибок компилятора, вы видите следующий сгенерированный 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 |
Исправление кода
Вы ознакомились со всеми определениями, и настало время исправить код. Запустите задачу Gradle runDebugExecutableNative в 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–2022 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