Spec-Zone.ru › Kotlin 1.4

Картирование примитивных типов данных из C

Последнее обновление 15 апреля 2019 г.
Примитивные типы данных из C и как они выглядят в Kotlin/Native

В этом руководстве мы узнаем, какие типы данных 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-битное значение.

END_OF_DOCUMENT_MARKER
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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API