Search code examples
kotlin-native

Kotlin Object to CPointer


I'm trying to pass a Kotlin Object to a C-Library which will pass that object over to a callback function. This function is written in Kotlin, thus I can use this Object and work with it. Howerver i could not find a way to convert a Kotlin Object to a CPointer. The only thing I found which may be what I need, is fun createKotlinObjectHolder(any: Any?): NativePtr and fun <reified T : Any> unwrapKotlinObjectHolder(holder: Any?): T. But even if I use the just created NativePtr to pass it over to the unwrapper function, it will fail with the error message unrecognized selector sent to instance 0xXYZ. What am I doing wrong or aren't those the functions I should use?

An example Code with libcURL:

fun writeCallback(ptr: CPointer<ByteVar>, ignored: ULong, size: ULong, userData: COpaquePointer?): ULong {
    if (userData != null) {
        unwrapKotlinObjectHolder<StringBuilder>(userData.rawValue).append("Hello")
    }
    return size
}

fun main() {
    ...
    val curl = curl_easy_init()
    ...
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, staticCFunction(::writeCallback))

    val stringBuilder = StringBuilder()
    val nativePtr = createKotlinObjectHolder(stringBuilder)
    val cPtr = interpretCPointer<CPointed>(nativePtr)
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, cPtr)
    ...
}

The code is compiled on macOS.


Solution

  • There is a StableRef class, that should be used to wrap Kotlin instances in such cases. This code should work correctly:

    fun writeCallback(ptr: CPointer<ByteVar>, ignored: ULong, size: ULong, userData: COpaquePointer?): ULong {
        if (userData != null) {
            userData.asStableRef<StringBuilder>().get().append("Hello")
        }
        return size
    }
    
    fun main() {
        /* get a curl handle */
        val curl = curl_easy_init();
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, staticCFunction(::writeCallback))
        val stringBuilder = StringBuilder()
        val stableRef = StableRef.create(stringBuilder)
        val cPtr = stableRef.asCPointer()
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, cPtr)
    
        curl_easy_perform(curl)
    
        curl_easy_cleanup(curl)
        stableRef.dispose()
    }
    

    See this page of the documentation for additional information.