Kotlin-native C-interop с проблемой leptonica

Я пытаюсь использовать leptonica библиотеку из Kotlin-native. Мне удалось создать клиб, и базовый код работает.

Моя проблема:

  • Я загружаю изображение с помощью pixRead() -> ОК,
  • Используйте изображение -> ОК,
  • Я не могу позвонить pixDestroy() на этом изображении -> FAIL.

Без вызова 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

person Xvolks    schedule 19.07.2019    source источник


Ответы (2)


Вы можете преобразовать свою переменную следующим образом:

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

Я нашел это решение в документации, его можно найти здесь

person Artyom Degtyarev    schedule 22.07.2019
comment
Не могли бы вы объяснить суть блока memScoped? Похоже, он используется для освобождения блока памяти, выделенного вызовом alloc. Здесь нет такого явного вызова. - person Xvolks; 22.07.2019
comment
Хм, согласно это Абзац, memScoped использовать незачем. Предположим, что его можно удалить, я отредактирую ответ. - person Artyom Degtyarev; 22.07.2019

Изменить: см. ответ @ ArtyomDegtyarev.

Отвечая на мой собственный вопрос, если кто-нибудь столкнется с той же проблемой.

Уловка состоит в том, чтобы создать массив указателей необходимого размера (здесь 1) и присвоить возвращаемый указатель правому элементу массива (здесь 0).

Затем вызовите toCValues() в массиве, чтобы получить эквивалент указателя & 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 здесь полезен, он был написан в источник я нашел.

ИЗМЕНИТЬ: я полагаю, что есть более естественный способ сделать это. Должен ли Kotlin-Native иметь способ получить адрес любой переменной? Я так думаю. Это ограничение языка, работа в стадии разработки? Если у кого-то есть ответ, я был бы рад его услышать.

person Xvolks    schedule 20.07.2019
comment
Привет! Вы можете попробовать этот фрагмент? pixDestroy(cValuesOf(pix)) - person Artyom Degtyarev; 22.07.2019
comment
@ArtyomDegtyarev работает нормально. Похоже, что он делает примерно то же самое, но намного лучше. Не стесняйтесь превратить свой комментарий в ответ, чтобы я мог его принять. - person Xvolks; 22.07.2019