Spec-Zone.ru › Kotlin 1.8

Картирование указателей на функции из C – учебник

Это третья публикация в серии. Самый первый учебник — Картирование примитивных типов данных из C. Также есть учебники Картирование структур и объединений из C и Картирование строк из C.

В этом учебнике мы узнаем, как:

  • Передать функцию Kotlin как указатель на функцию C

  • Использовать указатель на функцию C из Kotlin

Картирование типов указателей на функции из C

Лучший способ понять сопоставление между Kotlin и C — это попробовать небольшой пример. Объявите функцию, принимающую указатель на функцию в качестве параметра, и другую функцию, возвращающую указатель на функцию.

Kotlin/Native поставляется с инструментом cinterop; инструмент генерирует связи между языком C и Kotlin. Он использует файл .def для указания библиотеки C для импорта. Более подробные сведения об этом находятся в разделе Взаимодействие с библиотеками C.

Самый быстрый способ попробовать сопоставление API C — это поместить все объявления C в файл interop.def, не создавая никаких файлов .h или .c. Затем поместите объявления C в файл .def после специальной разделительной строки ---.

---

int myFun(int i) {
  return i+1;
}

typedef int  (*MyFun)(int);

void accept_fun(MyFun f) {
  f(42);
}

MyFun supply_fun() {
  return myFun;
}

Файла interop.def достаточно для компиляции и запуска приложения или для открытия его в IDE. Теперь пришло время создать файлы проекта, открыть проект в IntelliJ IDEA и запустить его.

Просмотр сгенерированных API Kotlin для библиотеки C

Хотя использовать командную строку, либо напрямую, либо комбинируя ее со скриптом (например, файлом .sh или .bat), можно, такой подход не масштабируется для больших проектов, содержащих сотни файлов и библиотек. В этом случае лучше использовать компилятор Kotlin/Native с системой сборки, так как она помогает загрузить и кэшировать двоичные файлы и библиотеки компилятора Kotlin/Native с транзитивными зависимостями и выполнить компиляцию и тесты. Kotlin/Native может использовать систему сборки Gradle через плагин kotlin-multiplatform.

Основы настройки IDE-совместимого проекта с Gradle мы рассмотрели в учебнике Базовое Kotlin/Native приложение. Обратите внимание на него, если вы ищете подробные начальные шаги и инструкции по созданию нового проекта Kotlin/Native и его открытию в IntelliJ IDEA. В этом учебнике мы рассмотрим расширенные сценарии использования Kotlin/Native, связанные с взаимодействием с C, и сборки с использованием Gradle.

Сначала создайте папку проекта. Все пути в этом учебнике будут относительными к этой папке. Иногда необходимо предварительно создать недостающие директории, прежде чем добавлять новые файлы.

Используйте следующий файл сборки Gradle:

plugins {
    kotlin("multiplatform") version "1.8.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.3"
  distributionType = Wrapper.DistributionType.BIN
}
plugins {
    id 'org.jetbrains.kotlin.multiplatform' version '1.8.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.3'
  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!")
  
  accept_fun(https://kotlinlang.org/*fix me */)
  val useMe = supply_fun()
}

Теперь вы готовы открыть проект в IntelliJ IDEA и посмотреть, как исправить пример проекта. Во время этого посмотрите, как функции C отображаются в объявлениях Kotlin/Native.

Указатели на функции C в Kotlin

С помощью IntelliJ IDEA's Перейти к | Объявлению или Использованиям или ошибок компилятора, посмотрите на следующие объявления функций C:

fun accept_fun(f: MyFun? /* = CPointer<CFunction<(Int) -> Int>>? */)
fun supply_fun(): MyFun? /* = CPointer<CFunction<(Int) -> Int>>? */

fun myFun(i: kotlin.Int): kotlin.Int

typealias MyFun = kotlinx.cinterop.CPointer<kotlinx.cinterop.CFunction<(kotlin.Int) -> kotlin.Int>>

typealias MyFunVar = kotlinx.cinterop.CPointerVarOf<lib.MyFun>

Вы видите, что функция typedef из C была преобразована в Kotlin typealias. Она использует тип CPointer<..> для представления параметров-указателей и CFunction<(Int)->Int> для представления сигнатуры функции. Для всех типов CPointer<CFunction<..> доступна функция-расширение оператора invoke, так что можно вызывать ее так же, как и любую другую функцию в Kotlin.

Передать функцию Kotlin как указатель на функцию C

Пришло время попробовать использовать функции C из программы Kotlin. Вызовите функцию accept_fun и передайте указатель на функцию C в лямбда-функцию Kotlin:

fun myFun() {
  accept_fun(staticCFunction<Int, Int> { it + 1 })
}

Этот вызов использует вспомогательную функцию staticCFunction{..} из Kotlin/Native для обертывания лямбда-функции Kotlin в указатель на функцию C. Она допускает только независящие и не захватывающие лямбда-функции. Например, она не может использовать локальную переменную из функции. Вы можете использовать только глобальные объявления. Бросание исключений из staticCFunction{..} приведет к непредсказуемым побочным эффектам. Важно убедиться, что ваш код не генерирует внезапные исключения.

Использовать указатель на функцию C из Kotlin

Следующим шагом является вызов указателя на функцию C из указателя на функцию C, полученного из вызова supply_fun().

fun myFun2() {
  val functionFromC = supply_fun() ?: error("No function is returned")
  
  functionFromC(42)
}

Kotlin преобразует тип возвращаемого значения указателя на функцию в объект CPointer<CFunction<..>. Необходимо явно проверить значение null сначала. Для этого в приведенном выше коде используется оператор эливис. Инструмент cinterop помогает нам преобразовать указатель на функцию C в легко вызываемый объект в Kotlin. Именно это мы сделали в последней строке.

Исправить код

Вы увидели все определения, и пришло время исправить и запустить код. Запустите задачу runDebugExecutableNative Gradle в IDE или воспользуйтесь следующей командой для запуска кода:

./gradlew runDebugExecutableNative

Код в файле hello.kt может выглядеть так:

import interop.*
import kotlinx.cinterop.*

fun main() {
  println("Hello Kotlin/Native!")
 
  val cFunctionPointer = staticCFunction<Int, Int> { it + 1 }
  accept_fun(cFunctionPointer)

  val funFromC = supply_fun() ?: error("No function is returned")
  funFromC(42)
}

Следующие шаги

Продолжайте изучать другие типы языка C и их представление в Kotlin/Native в следующих учебниках:

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

  • Картирование структур и объединений из C

  • Картирование строк из C

В документации по взаимодействию с C рассматриваются более сложные сценарии взаимодействия.

Последнее изменение: 10 января 2023
Картирование структур и объединений из C – учебник Картирование строк из 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-function-pointers-from-c.html

Spec-Zone.ru

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