函数指针与回调
源:Callbacks - Kotlin Native C Interop
函数指针是 C 语言实现回调机制的核心。Kotlin/Native 通过 staticCFunction 创建 C 兼容的函数指针,实现 Kotlin 代码被 C 代码调用。
函数指针基础
C 函数指针映射
C 函数指针在 Kotlin 中映射为 CPointer<CFunction<...>>:
c
// C 定义
typedef int (*Comparator)(int a, int b);
typedef void (*Callback)();
typedef char* (*Transformer)(const char* input);kotlin
@OptIn(ExperimentalForeignApi::class)
import kotlinx.cinterop.*
// Kotlin 类型别名
typealias Comparator = CPointer<CFunction<(Int, Int) -> Int>>
typealias Callback = CPointer<CFunction<() -> Unit>>
typealias Transformer = CPointer<CFunction<(CPointer<ByteVar>?) -> CPointer<ByteVar>?>>调用函数指针
kotlin
@OptIn(ExperimentalForeignApi::class)
fun callFunctionPointer(func: Comparator, a: Int, b: Int): Int {
// 直接调用
return func.invoke(a, b)
// 或使用操作符
// return func(a, b)
}staticCFunction创建回调
基础用法
staticCFunction 将 Kotlin 函数转换为 C 函数指针:
kotlin
@OptIn(ExperimentalForeignApi::class)
// 简单回调
val simpleCallback = staticCFunction<Unit> {
println("Callback invoked!")
}
// 带参数的回调
val printNumber = staticCFunction<Int, Unit> { num ->
println("Number: $num")
}
// 带返回值的回调
val doubleValue = staticCFunction<Int, Int> { x ->
x * 2
}使用示例:qsort
C 标准库的 qsort 需要函数指针作为比较器:
kotlin
@OptIn(ExperimentalForeignApi::class)
import platform.posix.*
// 升序比较器
val ascending = staticCFunction<CPointer<ByteVar>?, CPointer<ByteVar>?, Int> { a, b ->
val aVal = a!!.reinterpret<IntVar>().pointed.value
val bVal = b!!.reinterpret<IntVar>().pointed.value
aVal - bVal
}
// 降序比较器
val descending = staticCFunction<CPointer<ByteVar>?, CPointer<ByteVar>?, Int> { a, b ->
val aVal = a!!.reinterpret<IntVar>().pointed.value
val bVal = b!!.reinterpret<IntVar>().pointed.value
bVal - aVal
}
fun sortArray() {
val numbers = intArrayOf(5, 2, 8, 1, 9, 3)
nativeHeap.allocArray<IntVar>(numbers.size).let { array ->
// 复制数据
numbers.forEachIndexed { i, num -> array[i] = num }
// 调用 qsort
qsort(
array.reinterpret(),
numbers.size.toULong(),
sizeOf<IntVar>().toULong(),
ascending
)
// 打印结果
print("Sorted: ")
for (i in 0 until numbers.size) {
print("${array[i]} ")
}
println()
nativeHeap.free(array)
}
}闭包限制
staticCFunction的约束
staticCFunction 不能捕获外部变量:
kotlin
@OptIn(ExperimentalForeignApi::class)
fun wrongCallback(multiplier: Int) {
// ❌ 编译错误:无法捕获外部变量
val callback = staticCFunction<Int, Int> { x ->
x * multiplier // 错误!multiplier 是外部变量
}
}kotlin
@OptIn(ExperimentalForeignApi::class)
var globalMultiplier = 2
val correctCallback = staticCFunction<Int, Int> { x ->
x * globalMultiplier // OK:访问全局变量
}
fun useCallback() {
globalMultiplier = 3
// 使用 correctCallback
}原因解释
C 函数指针只是内存地址,无法携带闭包上下文。staticCFunction 生成的是真正的 C 函数,不是 Kotlin 闭包。
使用 StableRef 传递状态
StableRef 机制
许多 C API 提供 void* userData 参数用于传递自定义数据:
c
// C 风格 API
typedef void (*EventCallback)(int event, void* userData);
void register_callback(EventCallback callback, void* userData);使用 StableRef 在回调中访问 Kotlin 对象:
kotlin
@OptIn(ExperimentalForeignApi::class)
import kotlinx.cinterop.*
import kotlin.native.ref.*
// 1. 定义回调函数
val eventCallback = staticCFunction<Int, COpaquePointer?, Unit> { event, userData ->
if (userData != null) {
// 2. 恢复 Kotlin 对象
val handler = userData.asStableRef<EventHandler>().get()
handler.handleEvent(event)
}
}
class EventHandler {
var eventCount = 0
fun handleEvent(event: Int) {
eventCount++
println("Received event: $event (total: $eventCount)")
}
}
fun setupCallback() {
val handler = EventHandler()
// 3. 创建 StableRef
val stableRef = StableRef.create(handler)
// 4. 转换为 COpaquePointer 并注册
register_callback(eventCallback, stableRef.asCPointer())
// 5. 使用完毕后释放(重要!)
// stableRef.dispose()
}完整示例:Timer 回调
kotlin
@OptIn(ExperimentalForeignApi::class)
// C 定义:
// typedef void (*TimerCallback)(void* userData);
// void start_timer(int interval_ms, TimerCallback callback, void* userData);
// void stop_timer();
class TimerHandler {
private var tickCount = 0
fun onTick() {
tickCount++
println("Timer tick #$tickCount")
if (tickCount >= 10) {
println("Stopping timer")
stop_timer()
}
}
}
val timerCallback = staticCFunction<COpaquePointer?, Unit> { userData ->
userData?.asStableRef<TimerHandler>()?.get()?.onTick()
}
fun startTimer() {
val handler = TimerHandler()
val stableRef = StableRef.create(handler)
// 启动定时器
start_timer(1000, timerCallback, stableRef.asCPointer())
// 注意:需要在适当时机 dispose
// 例如在停止定时器后:
// stableRef.dispose()
}高级用法
多态回调
使用接口实现不同的回调处理:
kotlin
@OptIn(ExperimentalForeignApi::class)
interface NetworkCallback {
fun onData(data: ByteArray)
fun onError(code: Int)
}
val networkDataCallback = staticCFunction<CPointer<ByteVar>?, Int, COpaquePointer?, Unit> {
data, size, userData ->
userData?.asStableRef<NetworkCallback>()?.get()?.let { callback ->
if (data != null && size > 0) {
val bytes = ByteArray(size)
for (i in 0 until size) {
bytes[i] = data[i]
}
callback.onData(bytes)
}
}
}
val networkErrorCallback = staticCFunction<Int, COpaquePointer?, Unit> { code, userData ->
userData?.asStableRef<NetworkCallback>()?.get()?.onError(code)
}
class MyNetworkHandler : NetworkCallback {
override fun onData(data: ByteArray) {
println("Received ${data.size} bytes")
}
override fun onError(code: Int) {
println("Network error: $code")
}
}回调注册与注销
kotlin
@OptIn(ExperimentalForeignApi::class)
class CallbackManager<T> {
private var stableRef: StableRef<T>? = null
private var isRegistered = false
fun register(handler: T, register: (COpaquePointer) -> Unit) {
require(!isRegistered) { "Already registered" }
stableRef = StableRef.create(handler)
register(stableRef!!.asCPointer())
isRegistered = true
}
fun unregister(unregister: () -> Unit) {
if (isRegistered) {
unregister()
stableRef?.dispose()
stableRef = null
isRegistered = false
}
}
}
// 使用
val manager = CallbackManager<EventHandler>()
fun setup() {
val handler = EventHandler()
manager.register(handler) { userData ->
register_callback(eventCallback, userData)
}
}
fun cleanup() {
manager.unregister {
unregister_callback()
}
}异常处理
回调中的异常
staticCFunction 中抛出的异常会导致未定义行为:
kotlin
@OptIn(ExperimentalForeignApi::class)
val dangerousCallback = staticCFunction<Int, Int> { x ->
if (x < 0) {
throw IllegalArgumentException("Negative value") // ❌ 危险!
}
x * 2
}kotlin
@OptIn(ExperimentalForeignApi::class)
val safeCallback = staticCFunction<Int, Int> { x ->
try {
if (x < 0) {
println("Error: negative value")
return@staticCFunction 0
}
x * 2
} catch (e: Exception) {
println("Unexpected error: ${e.message}")
0
}
}错误码返回
kotlin
@OptIn(ExperimentalForeignApi::class)
// C: typedef int (*Operation)(int input, int* result);
// 返回 0 表示成功,非零表示错误码
val operationCallback = staticCFunction<Int, CPointer<IntVar>?, Int> { input, result ->
try {
if (input < 0) {
return@staticCFunction -1 // 错误码:非法参数
}
if (result == null) {
return@staticCFunction -2 // 错误码:空指针
}
result.pointed.value = input * input
0 // 成功
} catch (e: Exception) {
-999 // 未知错误
}
}常见模式
模式1:单次回调 (One-shot)
kotlin
@OptIn(ExperimentalForeignApi::class)
class AsyncOperation {
private var stableRef: StableRef<(Int) -> Unit>? = null
fun execute(onComplete: (Int) -> Unit) {
stableRef = StableRef.create(onComplete)
// C: void async_op(void (*callback)(int result, void* userData), void* userData)
async_op(oneShotCallback, stableRef!!.asCPointer())
}
companion object {
val oneShotCallback = staticCFunction<Int, COpaquePointer?, Unit> { result, userData ->
userData?.asStableRef<(Int) -> Unit>()?.let { ref ->
ref.get().invoke(result)
ref.dispose() // 立即释放
}
}
}
}
// 使用
AsyncOperation().execute { result ->
println("Operation completed with result: $result")
}模式2:持久回调 (Persistent)
kotlin
@OptIn(ExperimentalForeignApi::class)
class EventListener {
private var stableRef: StableRef<EventListener>? = null
private var isListening = false
fun onEvent(type: Int, data: String) {
println("Event $type: $data")
}
fun startListening() {
if (!isListening) {
stableRef = StableRef.create(this)
register_event_listener(persistentCallback, stableRef!!.asCPointer())
isListening = true
}
}
fun stopListening() {
if (isListening) {
unregister_event_listener()
stableRef?.dispose()
stableRef = null
isListening = false
}
}
companion object {
val persistentCallback = staticCFunction<Int, CPointer<ByteVar>?, COpaquePointer?, Unit> {
type, data, userData ->
userData?.asStableRef<EventListener>()?.get()?.let { listener ->
val dataStr = data?.toKString() ?: ""
listener.onEvent(type, dataStr)
}
}
}
}最佳实践
实践1:总是配对 create/dispose
kotlin
@OptIn(ExperimentalForeignApi::class)
class ResourceManager : AutoCloseable {
private val refs = mutableListOf<StableRef<*>>()
fun <T> createRef(obj: T): StableRef<T> {
val ref = StableRef.create(obj)
refs.add(ref)
return ref
}
override fun close() {
refs.forEach { it.dispose() }
refs.clear()
}
}
// 使用
ResourceManager().use { manager ->
val ref1 = manager.createRef(handler1)
val ref2 = manager.createRef(handler2)
// 自动释放
}实践2:类型安全的包装
kotlin
@OptIn(ExperimentalForeignApi::class)
inline fun <reified T> createSafeCallback(
crossinline handler: T.() -> Unit
): Pair<CPointer<CFunction<(COpaquePointer?) -> Unit>>, StableRef<T>> {
return staticCFunction<COpaquePointer?, Unit> { userData ->
userData?.asStableRef<T>()?.get()?.handler()
} to StableRef.create(handler)
}实践3:调试辅助
kotlin
@OptIn(ExperimentalForeignApi::class)
var callbackDebugEnabled = false
val debugCallback = staticCFunction<Int, Unit> { value ->
if (callbackDebugEnabled) {
println("[CALLBACK DEBUG] Value: $value, Thread: ${pthread_self()}")
}
// 实际逻辑...
}函数指针和回调是 C 互操作的关键机制。理解 staticCFunction 的限制并正确使用 StableRef 能够安全、高效地实现 Kotlin 与 C 的双向通信。