Отображение указателей на функции из C — руководство
Давайте рассмотрим, какие указатели на функции C доступны из Kotlin, а также изучим расширенные сценарии взаимодействия Kotlin/Native с C и сборки Gradle для мультиплатформенных проектов.
В этом руководстве вы:
Отображение типов указателей на функции из C
Чтобы понять, как типы Kotlin сопоставляются с типами C, объявим две функции: одну, принимающую указатель на функцию в качестве параметра, и другую, возвращающую указатель на функцию.
В первой части серии вы уже создали библиотеку C с необходимыми файлами. Для этого шага обновите объявления в файле interop.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.
Изучите сгенерированные API Kotlin для библиотеки C
Посмотрим, как указатели на функции C отображаются в Kotlin/Native, и обновим проект:
-
В
src/nativeMain/kotlinобновите файлhello.ktиз предыдущего руководства, заменив его содержимое следующим:import interop.* import kotlinx.cinterop.ExperimentalForeignApi @OptIn(ExperimentalForeignApi::class) fun main() { println("Hello Kotlin/Native!") accept_fun(/* fix me*/) val useMe = supply_fun() } -
Используйте команду IntelliJ IDEA Перейти к объявлению (Cmd + B/Ctrl + B), чтобы перейти к следующему сгенерированному API для функций C:
fun myFun(i: kotlin.Int): kotlin.Int fun accept_fun(f: kotlinx.cinterop.CPointer<kotlinx.cinterop.CFunction<(kotlin.Int) -> kotlin.Int>>? /* from: interop.MyFun? */) fun supply_fun(): kotlinx.cinterop.CPointer<kotlinx.cinterop.CFunction<(kotlin.Int) -> kotlin.Int>>? /* from: interop.MyFun? */
Как видите, указатели на функции C представлены в Kotlin с помощью CPointer<CFunction<...>>. Функция accept_fun() принимает указатель на функцию в качестве необязательного параметра, а supply_fun() возвращает указатель на функцию.
CFunction<(Int) -> Int> представляет сигнатуру функции, а CPointer<CFunction<...>>? — nullable-указатель на функцию. Для всех типов CPointer<CFunction<...>> доступна функция-расширение оператора .invoke(), позволяющая вызывать указатели на функции как обычные функции Kotlin.
Передача функции Kotlin в качестве указателя на функцию C
Пора попробовать использовать функции C из кода Kotlin. Вызовите функцию accept_fun() и передайте указатель на функцию C в лямбда-функцию Kotlin:
import interop.*
import kotlinx.cinterop.staticCFunction
import kotlinx.cinterop.ExperimentalForeignApi
@OptIn(ExperimentalForeignApi::class)
fun myFun() {
accept_fun(staticCFunction<Int, Int> { it + 1 })
}
В этом вызове используется вспомогательная функция staticCFunction {} из Kotlin/Native, чтобы преобразовать лямбда-функцию Kotlin в указатель на функцию C. Она поддерживает только несвязанные лямбда-функции, не захватывающие переменные. Например, такая функция не может захватывать локальную переменную из функции, а только глобально видимые объявления.
Убедитесь, что функция не выбрасывает исключения. Выбрасывание исключений из staticCFunction {} приводит к недетерминированным побочным эффектам.
Использование указателя на функцию C из Kotlin
Следующий шаг — вызвать указатель на функцию C, возвращённый вызовом supply_fun():
import interop.*
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.invoke
@OptIn(ExperimentalForeignApi::class)
fun myFun2() {
val functionFromC = supply_fun() ?: error("No function is returned")
functionFromC(42)
}
Kotlin преобразует возвращаемый тип указателя на функцию в nullable-объект CPointer<CFunction<>. Сначала нужно явно проверить его на null, поэтому в приведённом выше коде используется оператор Elvis. Инструмент cinterop позволяет вызывать указатель на функцию C как обычную функцию Kotlin: functionFromC(42).
Обновление кода Kotlin
Теперь, когда вы ознакомились со всеми определениями, попробуйте использовать их в проекте. Код в файле hello.kt может выглядеть так:
import interop.*
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.invoke
import kotlinx.cinterop.staticCFunction
@OptIn(ExperimentalForeignApi::class)
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)
}
Чтобы проверить, что всё работает как ожидается, запустите задачу Gradle runDebugExecutable<YourTargetName> в IDE или выполните в терминале команду консоли, например:
./gradlew runDebugExecutableMacosArm64
Следующий шаг
В следующей части серии вы узнаете, как строки сопоставляются между Kotlin и C:
См. также
Подробнее см. в документации Взаимодействие с C, где описаны более сложные сценарии.
© 2010–2026 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