From ddcbe68a5b3234510a63b93579efe783687e3ec7 Mon Sep 17 00:00:00 2001 From: Nikolay Igotti Date: Thu, 5 Apr 2018 19:30:10 +0300 Subject: [PATCH] Implement scope-local C pointer. (#1471) --- INTEROP.md | 16 ++++++++++++++++ .../src/main/kotlin/kotlinx/cinterop/Utils.kt | 3 +++ samples/androidNativeActivity/build.gradle | 3 ++- .../src/main/kotlin/renderer.kt | 8 ++++---- samples/gtk/src/main/kotlin/Main.kt | 2 +- .../src/main/kotlin/EchoServer.kt | 4 ++-- samples/uikit/src/main/kotlin/main.kt | 2 +- 7 files changed, 29 insertions(+), 9 deletions(-) diff --git a/INTEROP.md b/INTEROP.md index 64816c2216c..8b281ac7cfd 100644 --- a/INTEROP.md +++ b/INTEROP.md @@ -337,6 +337,22 @@ manually: In all cases the C string is supposed to be encoded as UTF-8. +### Scope-local pointers ### + +It is possible to create scope-stable pointer of C representation of `CValues` +instance using `CValues.ptr` extension property available under memScoped { ... }. +It allows to use APIs which requires C pointers with lifetime bound to certain `MemScope`. For example: +``` +memScoped { + items = arrayOfNulls?>(6) + arrayOf("one", "two").forEachIndexed { index, value -> items[index] = value.cstr.ptr } + menu = new_menu("Menu".cstr.ptr, items.toCValues().ptr) + ... +} +``` +In this example all values passed to the C API `new_menu()` have lifetime of innermost `memScope` +it belongs to. Once control flow will leave `memScoped` scope C pointers become invalid. + ### Passing and receiving structs by value ### When C function takes or returns a struct `T` by value, the corresponding diff --git a/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Utils.kt b/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Utils.kt index d6b06f3c1f2..14a9b87e9cc 100644 --- a/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Utils.kt +++ b/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Utils.kt @@ -412,6 +412,9 @@ class MemScope : ArenaBase() { val memScope: MemScope get() = this + + val CValues.ptr: CPointer + get() = this@ptr.getPointer(this@MemScope) } // TODO: consider renaming `memScoped` because it now supports `defer`. diff --git a/samples/androidNativeActivity/build.gradle b/samples/androidNativeActivity/build.gradle index 268786584a1..8f71734774f 100644 --- a/samples/androidNativeActivity/build.gradle +++ b/samples/androidNativeActivity/build.gradle @@ -15,6 +15,7 @@ buildscript { repositories { jcenter() + google() } apply plugin: 'konan' @@ -81,4 +82,4 @@ task buildApk(type: Copy) { dependsOn "assembleDebug" destinationDir outDir from 'build/outputs/apk' -} \ No newline at end of file +} diff --git a/samples/androidNativeActivity/src/main/kotlin/renderer.kt b/samples/androidNativeActivity/src/main/kotlin/renderer.kt index d79575e96cb..201879151ab 100644 --- a/samples/androidNativeActivity/src/main/kotlin/renderer.kt +++ b/samples/androidNativeActivity/src/main/kotlin/renderer.kt @@ -306,10 +306,10 @@ class Renderer(val parentArena: NativePlacement, val nativeActivity: ANativeActi } glFrontFace(GL_CW) - glVertexPointer(4, GL_FLOAT, 0, vertices.toFloatArray().toCValues().getPointer(this)) - glTexCoordPointer(2, GL_FLOAT, 0, texCoords.toFloatArray().toCValues().getPointer(this)) - glNormalPointer(GL_FLOAT, 0, normals.toFloatArray().toCValues().getPointer(this)) - glDrawElements(GL_TRIANGLES, triangles.size, GL_UNSIGNED_BYTE, triangles.toByteArray().toCValues().getPointer(this)) + glVertexPointer(4, GL_FLOAT, 0, vertices.toFloatArray().toCValues().ptr) + glTexCoordPointer(2, GL_FLOAT, 0, texCoords.toFloatArray().toCValues().ptr) + glNormalPointer(GL_FLOAT, 0, normals.toFloatArray().toCValues().ptr) + glDrawElements(GL_TRIANGLES, triangles.size, GL_UNSIGNED_BYTE, triangles.toByteArray().toCValues().ptr) glPopMatrix() diff --git a/samples/gtk/src/main/kotlin/Main.kt b/samples/gtk/src/main/kotlin/Main.kt index c6cc1da0983..6784869d622 100644 --- a/samples/gtk/src/main/kotlin/Main.kt +++ b/samples/gtk/src/main/kotlin/Main.kt @@ -54,7 +54,7 @@ fun gtkMain(args: Array): Int { g_signal_connect(app, "activate", staticCFunction(::activate)) val status = memScoped { g_application_run(app.reinterpret(), - args.size, args.map { it.cstr.getPointer(memScope) }.toCValues()) + args.size, args.map { it.cstr.ptr }.toCValues()) } g_object_unref(app) return status diff --git a/samples/nonBlockingEchoServer/src/main/kotlin/EchoServer.kt b/samples/nonBlockingEchoServer/src/main/kotlin/EchoServer.kt index 98ee2530f71..b6a62096dff 100644 --- a/samples/nonBlockingEchoServer/src/main/kotlin/EchoServer.kt +++ b/samples/nonBlockingEchoServer/src/main/kotlin/EchoServer.kt @@ -56,7 +56,7 @@ fun main(args: Array) { val bufferLength = 100L val buffer = allocArray(bufferLength) val connectionIdString = "#${++connectionId}: ".cstr - val connectionIdBytes = connectionIdString.getPointer(this) + val connectionIdBytes = connectionIdString.ptr try { while (true) { @@ -212,4 +212,4 @@ inline fun Long.ensureUnixCallResult(predicate: (Long) -> Boolean): Long { throw Error(getUnixError()) } return this -} \ No newline at end of file +} diff --git a/samples/uikit/src/main/kotlin/main.kt b/samples/uikit/src/main/kotlin/main.kt index 11803196a60..cc7f17622ad 100644 --- a/samples/uikit/src/main/kotlin/main.kt +++ b/samples/uikit/src/main/kotlin/main.kt @@ -5,7 +5,7 @@ import platform.UIKit.* fun main(args: Array) { memScoped { val argc = args.size + 1 - val argv = (arrayOf("konan") + args).map { it.cstr.getPointer(memScope) }.toCValues() + val argv = (arrayOf("konan") + args).map { it.cstr.ptr }.toCValues() autoreleasepool { UIApplicationMain(argc, argv, null, NSStringFromClass(AppDelegate))