You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Let's explore which C function pointers are visible from Kotlin and examine advanced C interop-related use cases of
22
+
Kotlin/Native and [multiplatform](gradle-configure-project.md#targeting-multiple-platforms) Gradle builds.
22
23
23
-
-[Pass Kotlin function as C function pointer](#pass-kotlin-function-as-c-function-pointer)
24
-
-[Use C function pointer from Kotlin](#use-the-c-function-pointer-from-kotlin)
24
+
In this tutorial, you'll:
25
+
26
+
*[Learn how to pass Kotlin function as a C function pointer](#pass-kotlin-function-as-a-c-function-pointer)
27
+
*[Use C function pointers from Kotlin](#use-the-c-function-pointer-from-kotlin)
25
28
26
29
## Mapping function pointer types from C
27
30
28
-
The best way to understand the mapping between Kotlin and C is to try a tiny
29
-
example. Declare a function that accepts a function pointer as a parameter and
30
-
another function that returns a function pointer.
31
+
To understand the mapping between Kotlin and C, let's declare two functions: one that accepts a function pointer as a
32
+
parameter and another that returns a function pointer.
31
33
32
-
Kotlin/Native comes with the `cinterop` tool; the tool generates bindings between the C language and Kotlin.
33
-
It uses a `.def` file to specify a C library to import. More details on this are
34
-
in [Interop with C Libraries](native-c-interop.md).
35
-
36
-
The quickest way to try out C API mapping is to have all C declarations in the
37
-
`interop.def` file, without creating any `.h` of `.c` files at all. Then place the C declarations
38
-
in a `.def` file after the special `---` separator line:
34
+
In the [first part of the series](mapping-primitive-data-types-from-c.md) of the series, you've already created a C library with the
35
+
necessary files. For this step, update the declarations in the `interop.def` file after the `---` separator:
39
36
40
37
```c
41
38
@@ -54,216 +51,121 @@ void accept_fun(MyFun f) {
54
51
MyFun supply_fun() {
55
52
return myFun;
56
53
}
57
-
58
54
```
59
55
60
-
The `interop.def` file is enough to compile and run the application or open it in an IDE.
61
-
Now it is time to create project files, open the project in
62
-
[IntelliJ IDEA](https://jetbrains.com/idea) and run it.
56
+
The `interop.def` file provides everything necessary to compile, run, or open the application in an IDE.
63
57
64
58
## Inspect generated Kotlin APIs for a C library
65
59
66
-
While it is possible to use the command line, either directly or
67
-
by combining it with a script file (such as `.sh` or `.bat` file), this approach doesn't
68
-
scale well for big projects that have hundreds of files and libraries.
69
-
It is then better to use the Kotlin/Native compiler with a build system, as it
70
-
helps to download and cache the Kotlin/Native compiler binaries and libraries with
71
-
transitive dependencies and run the compiler and tests.
72
-
Kotlin/Native can use the [Gradle](https://gradle.org) build system through the [kotlin-multiplatform](gradle-configure-project.md#targeting-multiple-platforms) plugin.
73
-
74
-
We covered the basics of setting up an IDE compatible project with Gradle in the
75
-
[Get started with Kotlin/Native](native-get-started.md#using-gradle)
76
-
tutorial. Please check it out if you are looking for detailed first steps
77
-
and instructions on how to start a new Kotlin/Native project and open it in IntelliJ IDEA.
78
-
In this tutorial, we'll look at the advanced C interop related usages of Kotlin/Native
79
-
and [multiplatform](gradle-configure-project.md#targeting-multiple-platforms)
80
-
builds with Gradle.
81
-
82
-
First, create a project folder. All the paths in this tutorial will be relative to this folder. Sometimes
83
-
the missing directories will have to be created before any new files can be added.
60
+
Let's see how C function pointers are mapped into Kotlin/Native and update your project:
84
61
85
-
Use the following `build.gradle(.kts)` Gradle build file:
62
+
1. In `src/nativeMain/kotlin`, update your `hello.kt` file from the [previous tutorial](mapping-struct-union-types-from-c.md)
63
+
with the following content:
86
64
87
-
<tabs group="build-script">
88
-
<tab title="Kotlin" group-key="kotlin">
89
-
90
-
```kotlin
91
-
plugins {
92
-
kotlin("multiplatform") version "%kotlinVersion%"
93
-
}
65
+
```kotlin
66
+
import interop.*
67
+
import kotlinx.cinterop.ExperimentalForeignApi
68
+
69
+
@OptIn(ExperimentalForeignApi::class)
70
+
fun main() {
71
+
println("Hello Kotlin/Native!")
72
+
73
+
accept_fun(/* fix me*/)
74
+
val useMe = supply_fun()
75
+
}
76
+
```
94
77
95
-
repositories {
96
-
mavenCentral()
97
-
}
78
+
2. Use the IntelliJ IDEA's [Go to declaration](https://www.jetbrains.com/help/rider/Navigation_and_Search__Go_to_Declaration.html)
79
+
command (<shortcut>Cmd + B</shortcut>/<shortcut>Ctrl + B</shortcut>) to navigate to the following generated API
80
+
for C functions:
98
81
99
-
kotlin {
100
-
linuxX64("native") { // on Linux
101
-
// macosX64("native") { // on x86_64 macOS
102
-
// macosArm64("native") { // on Apple Silicon macOS
You see that the function's `typedef` from C has been turned into Kotlin `typealias`. It uses `CPointer<..>` type
199
-
to represent the pointer parameters, and `CFunction<(Int)->Int>` to represent the function signature.
200
-
Thereis an `invoke` operator extension function available for all `CPointer<CFunction<..>` types, so that
201
-
it is possible to call it as you would call any other function inKotlin.
135
+
Kotlin turns the function pointer return type into a nullable `CPointer<CFunction<>` object. You need to first explicitly
136
+
check for `null`, which is why the [Elvis operator](null-safety.md) is used in the code above.
137
+
The `cinterop` tool allows you to call a C function pointer as a regular Kotlin function call: `functionFromC(42)`.
202
138
203
-
## PassKotlinfunction asC function pointer
139
+
## Update Kotlin code
204
140
205
-
Itis the time to try using C functions from the Kotlin program. Call the `accept_fun`
206
-
function and pass the C function pointer to a Kotlin lambda:
141
+
Now that you've seen all the definitions, try to use them in your project.
142
+
The code in the `hello.kt` file may look like this:
207
143
208
144
```kotlin
209
-
funmyFun() {
210
-
accept_fun(staticCFunction<Int, Int> { it +1 })
211
-
}
212
-
213
-
```
214
-
215
-
This call uses the `staticCFunction{..}` helper function from Kotlin/Native to wrap a Kotlin lambda function into a C function pointer.
216
-
It only allows having unbound and non-capturing lambda functions. For example, it isnot able
217
-
to use a local variable from the function. You may only use globally visible declarations.
218
-
219
-
Itis vital to make sure that the function does notthrow any exceptions.
220
-
Throwing exceptions from a `staticCFunction{..}` will end up in non-deterministic side-effects.
145
+
importinterop.*
146
+
importkotlinx.cinterop.ExperimentalForeignApi
147
+
importkotlinx.cinterop.invoke
148
+
importkotlinx.cinterop.staticCFunction
221
149
222
-
## Use the C function pointer from Kotlin
150
+
@OptIn(ExperimentalForeignApi::class)
151
+
funmain() {
152
+
println("Hello Kotlin/Native!")
223
153
224
-
The next step is to call a C function pointer from a C pointer that you have from the `supply_fun()` call:
154
+
val cFunctionPointer = staticCFunction<Int, Int> { it +1 }
155
+
accept_fun(cFunctionPointer)
225
156
226
-
```kotlin
227
-
funmyFun2() {
228
-
val functionFromC = supply_fun() ?: error("No function is returned")
229
-
230
-
functionFromC(42)
157
+
val funFromC = supply_fun() ?: error("No function is returned")
158
+
funFromC(42)
231
159
}
232
-
233
160
```
234
161
235
-
Kotlin turns the function pointer return type into a nullable `CPointer<CFunction<..>` object. Thereis the need
236
-
to explicitly check for `null` first. The [elvis operator](null-safety.md) for that in the code above.
237
-
The `cinterop` tool helps us to turn a C function pointer into an easy to call objectin Kotlin. This is
238
-
what we did on the last line.
239
-
240
-
## Fix the code
241
-
242
-
You've seen all definitions and it is time to fix and run the code.
243
-
Run the `runDebugExecutableNative` Gradle task [in the IDE](native-get-started.md)
162
+
To verify that everything works as expected, run the `runDebugExecutableNative` Gradle task [in IDE](native-get-started.md#build-and-run-the-application)
244
163
or use the following command to run the code:
245
164
246
165
```bash
247
166
./gradlew runDebugExecutableNative
248
167
```
249
168
250
-
The code in the `hello.kt` file may look like this:
251
-
252
-
```kotlin
253
-
import interop.*
254
-
import kotlinx.cinterop.*
255
-
256
-
fun main() {
257
-
println("Hello Kotlin/Native!")
258
-
259
-
val cFunctionPointer = staticCFunction<Int, Int> { it + 1 }
260
-
accept_fun(cFunctionPointer)
261
-
262
-
val funFromC = supply_fun() ?: error("No function is returned")
263
-
funFromC(42)
264
-
}
265
-
```
266
-
267
169
## Next step
268
170
269
171
In the next part of the series, you'll learn how strings are mapped between Kotlin and C:
0 commit comments