Skip to content

函数指针与回调

源: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 的双向通信。