Kotlin-native C-interop 与 leptonica 问题

Kotlin-native C-interop with leptonica issue

我正在尝试使用来自 Kotlin-native 的 leptonica 库。 我已经成功创建了 klib 并且基本代码正在运行。

我的问题是:

如果没有 pixDestroy() 调用,程序将按预期工作,只是它会泄漏内存。

基本上我想这样获取指针的地址(source):

    pixt1 = pixRead("/tmp/lept/dewmod/0020.png");
    pixWrite("/tmp/lept/dewtest/006.png", pixt1, IFF_PNG);
    pixDestroy(&pixt1);

我的代码如下:

import leptonica.* 
import kotlinx.cinterop.* 


fun doSomethingWithPix(pix: PIX) {
    // bla
    println(pix.xres) 
}

fun usePix(filename: String) {
    val pix = pixRead(filename) ?: throw NullPointerException("Pix is null")
    doSomethingWithPix(pix.pointed)
    pixDestroy(pix ???) // Expect a CValuesRef<CPointerVar<PIX>> how to create/cast that ? 
}

fun main() {
    usePix("test.png") }
}

这里记录的是我的 leptonica.def 文件。

headers = leptonica/allheaders.h
headerFilter = leptonica/allheaders.h
package = leptonica

compilerOpts.osx = -I/usr/local/opt/include
linkerOpts.osx = -L/usr/local/opt/lib -llept

build.gradle

plugins {
    id 'org.jetbrains.kotlin.multiplatform' version '1.3.41'
}
repositories {
    mavenCentral()
}
kotlin {
    // For ARM, should be changed to iosArm32 or iosArm64
    // For Linux, should be changed to e.g. linuxX64
    // For MacOS, should be changed to e.g. macosX64
    // For Windows, should be changed to e.g. mingwX64
    macosX64("macos") {
        compilations.main.cinterops {
            png
            tesseract
            leptonica
        }

        binaries {
            executable {
                // Change to specify fully qualified name of your application's entry point:
               entryPoint = 'sample.main'
                // Specify command-line arguments, if necessary:
                runTask?.args('')
            }
        }
    }
//    iosArm64("ios64") {
//        compilations.main.cinterops {
//            png
//        }
//
//        binaries {
//            executable {
//                // Change to specify fully qualified name of your application's entry point:
//               entryPoint = 'sample.main'
//                // Specify command-line arguments, if necessary:
//                runTask?.args('')
//            }
//        }
//    }
    sourceSets {
        // Note: To enable common source sets please comment out 'kotlin.import.noCommonSourceSets' property
        // in gradle.properties file and re-import your project in IDE.
        macosMain {
        }
        macosTest {
        }
    }

}

// Use the following Gradle tasks to run your application:
// :runReleaseExecutableMacos - without debug symbols
// :runDebugExecutableMacos - with debug symbols

编辑:参见@ArtyomDegtyarev 的回答。

如果有人遇到同样的问题,请回答我自己的问题。

诀窍是创建一个所需大小的指针数组(此处为 1)并将返回的指针分配给数组的正确元素(此处为 0).

然后在数组上调用 toCValues() 以获得 &pointer C 等价物。

fun usePix(filename: String) {
    memScoped {
        val ppix = arrayOfNulls<CPointer<PIX>?>(1)
        val pix = pixRead(filename) ?: throw NullPointerException("Pix is null")
        ppix[0] = pix
        doSomethingWithPix(pix.pointed)
        pixDestroy(ppix.toCValues())
    }
}

我不确定 memScoped 块在这里是否有用,它写在我发现的 source 中。

编辑:我希望有更自然的方法来做到这一点。 Kotlin-Native 应该有办法获取任何变量的地址吗?我认同。它是语言的限制,还是一项正在进行的工作?如果有人知道答案,我会很高兴听到。

您可以像这样转换您的变量:

fun usePix(filename: String) {
    val pix = pixRead(filename) ?: throw NullPointerException("Pix is null")
    doSomethingWithPix(pix.pointed)
    pixDestroy(cValuesOf(pix))
}

我在文档中找到了这个解决方案,可以找到here