Картирование примитивных типов данных из 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++ на Stack Overflow содержится более подробная информация об этом.
Для каждой группы файлов .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 и многоплатформенных сборок с Gradle для взаимодействия с C.
Сначала создайте папку проекта. Все пути в этом учебнике будут относительными к этой папке. Иногда необходимо создать отсутствующие директории перед добавлением новых файлов.
Используйте следующий файл сборки Gradle:
plugins {
kotlin("multiplatform") version "1.9.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.6"
distributionType = Wrapper.DistributionType.BIN
}
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.9.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.6'
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 |
Исправление кода
Вы увидели все определения, и пришло время исправить код. Запустите задачу 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–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