Passing int* to C from Kotlin/Native
Today, suddenly, a friend of mine gave me an easy question - how can one pass an int pointer to a C function from Kotlin/Native. To my shame, I did not have the answer. Now I do
The Short Answer
memScoped {
val q = alloc<IntVar>()
q.value = 123
function_from_c(q.ptr)
}
The Long Answer
We need several files for the demo project. We will use Gradle Multiplatform Project with Kotlin 1.3.11 and IntelliJ IDEA. Yes, it will be enough to work with (!) Kotlin/Native
C Interop
It’s enough to create only a file.def file with C code inside (after --- line):
---
int function_from_c(int* x) {
return *x = *x + 10;
}
The cinterop tool generates the following Kotlin code interop from it:
fun function_from_c(x: CValuesRef<IntVar>?): Int
Right here we have a question - how to create an object of CValuesRef<IntVar> type?
The Solution
- Use
CPointer<IntVar>, which is subtype ofCValuesRef<IntVar> - Call the
.ptrextension property on theIntVarto get aCPointer<IntVar>instance - Create a
IntVarinstance via thealloc<T>extension function on aNativePlacementinstance - Get a
NativePlacementinstance from thememScoped { ... }block receiver - Use the
.valueproperty onIntVarto get/setIntvalue
Full code of the main.kt is as follows. Mind the imports from kotlinx.cinterop:
import file.function_from_c
import kotlinx.cinterop.IntVar
import kotlinx.cinterop.alloc
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.ptr
import kotlinx.cinterop.value
fun main(args: Array<String>) {
println("Hello!")
memScoped {
val q: IntVar = alloc<IntVar>()
q.value = 123
val z = function_from_c(q.ptr)
println(z) // return value, C int
println(q.value) // updated int* from C
}
}
Gradle Project Setup
Let’s create a demo project with Gradle for IntelliJ IDEA. You need an empty Gradle
project to start. I called gradle init command from console to get it in a new folder
(it was Gradle 5.1 in my case).
As an alternative, you may create a project with File | New | Project... menu too.
Next, paste the following to the build.gradle:
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.3.11'
}
kotlin {
targets {
/// use presets.mingwX64 for Windows
/// use presets.linuxX64 for Linux
fromPreset(presets.macosX64, 'native') {
compilations.main {
outputKinds 'executable'
cinterops {
myInterop {
defFile project.file("src/native/file.def")
}
}
}
}
}
sourceSets {
nativeMain {
kotlin.srcDir('src/native')
}
}
}
Place the main.kt and file.def files into the src/native folder. Now you may open the project in IntelliJ IDEA
by pointing it to the build.gradle file (use Java 1.8 in the Gradle Import dialog).
I use Kotlin Multiplatform Project. You may find in the demo sources on my GitHub.
Take a look to the C Interop article for more information on the Kotlin/Native interop with C. An Interop With C tutorials are a good read to see how other types are mapped between C and Kotlin/Native.
Have fun!