Сопоставление примитивных типов данных из C – учебник
В этом учебнике вы узнаете, какие типы данных C видны в Kotlin/Native и наоборот. Вы будете:
Узнать, какие типы данных используются в языке C.
Создать небольшую библиотеку на 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 и плагина multiplatform для взаимодействия с C.
Сначала создайте папку проекта. Все пути в этом учебнике будут относительными к этой папке. Иногда отсутствующие каталоги необходимо создать перед добавлением новых файлов.
Используйте следующий build.gradle(.kts) файл Gradle:
plugins {
kotlin("multiplatform") version "1.7.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.7.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 (Перейти | Объявление) или ошибок компилятора вы видите сгенерированный 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–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