KT-26712: styles INTEROP.md for Kotlin web site (#2039)

This commit is contained in:
Alexander Prendota
2018-09-11 17:30:59 +03:00
committed by Nikolay Igotti
parent 1b6c36c860
commit 754ea1d2d5
+202 -35
View File
@@ -26,14 +26,28 @@ Build the dependencies and the compiler (see `README.md`).
Prepare stubs for the system sockets library: Prepare stubs for the system sockets library:
cd samples/socket <div class="sample" markdown="1" theme="idea" mode="shell">
../../dist/bin/cinterop -def src/main/c_interop/sockets.def \
-o sockets ```bash
cd samples/socket
../../dist/bin/cinterop -def src/main/c_interop/sockets.def \
-o sockets
```
</div>
Compile the echo server: Compile the echo server:
../../dist/bin/kotlinc src/main/kotlin/EchoServer.kt \ <div class="sample" markdown="1" theme="idea" mode="shell">
-library sockets -o EchoServer
```bash
../../dist/bin/kotlinc src/main/kotlin/EchoServer.kt \
-library sockets -o EchoServer
```
</div>
This whole process is automated in `build.sh` script, which also support cross-compilation This whole process is automated in `build.sh` script, which also support cross-compilation
to supported cross-targets with `TARGET=raspberrypi ./build.sh` (`cross_dist` target must to supported cross-targets with `TARGET=raspberrypi ./build.sh` (`cross_dist` target must
@@ -41,11 +55,23 @@ be executed first).
Run the server: Run the server:
./EchoServer.kexe 3000 & <div class="sample" markdown="1" theme="idea" mode="shell">
```bash
./EchoServer.kexe 3000 &
```
</div>
Test the server by connecting to it, for example with telnet: Test the server by connecting to it, for example with telnet:
telnet localhost 3000 <div class="sample" markdown="1" theme="idea" mode="shell">
```bash
telnet localhost 3000
```
</div>
Write something to console and watch server echoing it back. Write something to console and watch server echoing it back.
@@ -54,14 +80,27 @@ Write something to console and watch server echoing it back.
To create bindings for a new library, start by creating `.def` file. To create bindings for a new library, start by creating `.def` file.
Structurally it's a simple property file, looking like this: Structurally it's a simple property file, looking like this:
<div class="sample" markdown="1" theme="idea" mode="c">
```c
headers = zlib.h
compilerOpts = -std=c99
```
</div>
headers = zlib.h
compilerOpts = -std=c99
Then run `cinterop` tool with something like (note that for host libraries not included Then run `cinterop` tool with something like (note that for host libraries not included
in sysroot search paths for headers may be needed): in sysroot search paths for headers may be needed):
cinterop -def zlib.def -copt -I/opt/local/include -o zlib <div class="sample" markdown="1" theme="idea" mode="shell">
```bash
cinterop -def zlib.def -copt -I/opt/local/include -o zlib
```
</div>
This command will produce `zlib.klib` compiled library and This command will produce `zlib.klib` compiled library and
`zlib-build/kotlin` directory containing Kotlin source code for the library. `zlib-build/kotlin` directory containing Kotlin source code for the library.
@@ -104,10 +143,15 @@ The globs are applied to the header paths relative to the appropriate include
path elements, e.g. `time.h` or `curl/curl.h`. So if the library is usually path elements, e.g. `time.h` or `curl/curl.h`. So if the library is usually
included with `#include <SomeLbrary/Header.h>`, then it would probably be included with `#include <SomeLbrary/Header.h>`, then it would probably be
correct to filter headers with correct to filter headers with
```
<div class="sample" markdown="1" theme="idea" mode="c">
```c
headerFilter = SomeLibrary/** headerFilter = SomeLibrary/**
``` ```
</div>
If `headerFilter` is not specified, then all headers are included. If `headerFilter` is not specified, then all headers are included.
#### Filtering by module maps #### Filtering by module maps
@@ -119,12 +163,18 @@ describes the correspondence between header files and modules. When the module
maps are available, the headers from the modules that are not included directly maps are available, the headers from the modules that are not included directly
can be filtered out using experimental `excludeDependentModules` option of the can be filtered out using experimental `excludeDependentModules` option of the
`.def` file: `.def` file:
```
<div class="sample" markdown="1" theme="idea" mode="c">
```c
headers = OpenGL/gl.h OpenGL/glu.h GLUT/glut.h headers = OpenGL/gl.h OpenGL/glu.h GLUT/glut.h
compilerOpts = -framework OpenGL -framework GLUT compilerOpts = -framework OpenGL -framework GLUT
excludeDependentModules = true excludeDependentModules = true
``` ```
</div>
When both `excludeDependentModules` and `headerFilter` are used, they are When both `excludeDependentModules` and `headerFilter` are used, they are
applied as intersection. applied as intersection.
@@ -136,7 +186,9 @@ additional header file with these declarations, you can include them directly
to the end of the `.def` file, after separating line, containing only the to the end of the `.def` file, after separating line, containing only the
separator sequence `---`: separator sequence `---`:
``` <div class="sample" markdown="1" theme="idea" mode="c">
```c
headers = errno.h headers = errno.h
--- ---
@@ -146,6 +198,8 @@ static inline int getErrno() {
} }
``` ```
</div>
Note that this part of the `.def` file is treated as part of the header file, so Note that this part of the `.def` file is treated as part of the header file, so
functions with body should be declared as `static`. functions with body should be declared as `static`.
The declarations are parsed after including the files from `headers` list. The declarations are parsed after including the files from `headers` list.
@@ -157,11 +211,15 @@ rather that assuming it is available within the user environment.
To include a static library into `.klib` use `staticLibrary` and `libraryPaths` To include a static library into `.klib` use `staticLibrary` and `libraryPaths`
clauses. For example: clauses. For example:
``` <div class="sample" markdown="1" theme="idea" mode="c">
```c
staticLibraries = libfoo.a staticLibraries = libfoo.a
libraryPaths = /opt/local/lib /usr/local/opt/curl/lib libraryPaths = /opt/local/lib /usr/local/opt/curl/lib
``` ```
</div>
When given the above snippet the `cinterop` tool will search `libfoo.a` in When given the above snippet the `cinterop` tool will search `libfoo.a` in
`/opt/local/lib` and `/usr/local/opt/curl/lib`, and if found include the `/opt/local/lib` and `/usr/local/opt/curl/lib`, and if found include the
library binary into `klib`. library binary into `klib`.
@@ -205,14 +263,21 @@ C null pointer is represented as Kotlin's `null`, and the pointer type
`CPointer<T>` is not nullable, but the `CPointer<T>?` is. The values of this `CPointer<T>` is not nullable, but the `CPointer<T>?` is. The values of this
type support all Kotlin operations related to handling `null`, e.g. `?:`, `?.`, type support all Kotlin operations related to handling `null`, e.g. `?:`, `?.`,
`!!` etc: `!!` etc:
```
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val path = getenv("PATH")?.toKString() ?: "" val path = getenv("PATH")?.toKString() ?: ""
``` ```
</div>
Since the arrays are also mapped to `CPointer<T>`, it supports `[]` operator Since the arrays are also mapped to `CPointer<T>`, it supports `[]` operator
for accessing values by index: for accessing values by index:
``` <div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
fun shift(ptr: CPointer<BytePtr>, length: Int) { fun shift(ptr: CPointer<BytePtr>, length: Int) {
for (index in 0 .. length - 2) { for (index in 0 .. length - 2) {
ptr[index] = ptr[index + 1] ptr[index] = ptr[index + 1]
@@ -220,6 +285,8 @@ fun shift(ptr: CPointer<BytePtr>, length: Int) {
} }
``` ```
</div>
The `.pointed` property for `CPointer<T>` returns the lvalue of type `T`, The `.pointed` property for `CPointer<T>` returns the lvalue of type `T`,
pointed by this pointer. The reverse operation is `.ptr`: it takes the lvalue pointed by this pointer. The reverse operation is `.ptr`: it takes the lvalue
and returns the pointer to it. and returns the pointer to it.
@@ -230,48 +297,79 @@ the Kotlin binding accepts any `CPointer`.
Casting any pointer (including `COpaquePointer`) can be done with Casting any pointer (including `COpaquePointer`) can be done with
`.reinterpret<T>`, e.g.: `.reinterpret<T>`, e.g.:
```
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val intPtr = bytePtr.reinterpret<IntVar>() val intPtr = bytePtr.reinterpret<IntVar>()
``` ```
</div>
or or
```
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val intPtr: CPointer<IntVar> = bytePtr.reinterpret() val intPtr: CPointer<IntVar> = bytePtr.reinterpret()
``` ```
</div>
As in C, those reinterpret casts are unsafe and could potentially lead to As in C, those reinterpret casts are unsafe and could potentially lead to
subtle memory problems in an application. subtle memory problems in an application.
Also there are unsafe casts between `CPointer<T>?` and `Long` available, Also there are unsafe casts between `CPointer<T>?` and `Long` available,
provided by `.toLong()` and `.toCPointer<T>()` extension methods: provided by `.toLong()` and `.toCPointer<T>()` extension methods:
```
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val longValue = ptr.toLong() val longValue = ptr.toLong()
val originalPtr = longValue.toCPointer<T>() val originalPtr = longValue.toCPointer<T>()
``` ```
</div>
Note that if the type of the result is known from the context, the type argument Note that if the type of the result is known from the context, the type argument
can be omitted as usual due to type inference. can be omitted as usual due to type inference.
### Memory allocation ### ### Memory allocation ###
The native memory can be allocated using `NativePlacement` interface, e.g. The native memory can be allocated using `NativePlacement` interface, e.g.
```
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val byteVar = placement.alloc<ByteVar>() val byteVar = placement.alloc<ByteVar>()
``` ```
</div>
or or
```
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val bytePtr = placement.allocArray<ByteVar>(5): val bytePtr = placement.allocArray<ByteVar>(5):
``` ```
</div>
The most "natural" placement is object `nativeHeap`. The most "natural" placement is object `nativeHeap`.
It corresponds to allocating native memory with `malloc` and provides additional It corresponds to allocating native memory with `malloc` and provides additional
`.free()` operation to free allocated memory: `.free()` operation to free allocated memory:
``` <div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val buffer = nativeHeap.allocArray<ByteVar>(size) val buffer = nativeHeap.allocArray<ByteVar>(size)
<use buffer> <use buffer>
nativeHeap.free(buffer) nativeHeap.free(buffer)
``` ```
</div>
However the lifetime of allocated memory is often bound to lexical scope. However the lifetime of allocated memory is often bound to lexical scope.
It is possible to define such scope with `memScoped { ... }`. It is possible to define such scope with `memScoped { ... }`.
Inside the braces the temporary placement is available as implicit receiver, Inside the braces the temporary placement is available as implicit receiver,
@@ -280,7 +378,10 @@ and the allocated memory will be automatically freed after leaving the scope.
For example, the C function returning values through pointer parameters can be For example, the C function returning values through pointer parameters can be
used like used like
```
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val fileSize = memScoped { val fileSize = memScoped {
val statBuf = alloc<statStruct>() val statBuf = alloc<statStruct>()
val error = stat("/", statBuf.ptr) val error = stat("/", statBuf.ptr)
@@ -288,6 +389,8 @@ val fileSize = memScoped {
} }
``` ```
</div>
### Passing pointers to bindings ### ### Passing pointers to bindings ###
Although C pointers are mapped to `CPointer<T>` type, the C function Although C pointers are mapped to `CPointer<T>` type, the C function
@@ -309,18 +412,28 @@ methods are provided:
For example: For example:
C: C:
```
<div class="sample" markdown="1" theme="idea" mode="c">
```c
void foo(int* elements, int count); void foo(int* elements, int count);
... ...
int elements[] = {1, 2, 3}; int elements[] = {1, 2, 3};
foo(elements, 3); foo(elements, 3);
``` ```
</div>
Kotlin: Kotlin:
```
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
foo(cValuesOf(1, 2, 3), 3) foo(cValuesOf(1, 2, 3), 3)
``` ```
</div>
### Working with the strings ### ### Working with the strings ###
Unlike other pointers, the parameters of type `const char*` are represented as Unlike other pointers, the parameters of type `const char*` are represented as
@@ -334,19 +447,33 @@ manually:
* `val String.cstr: CValuesRef<ByteRef>`. * `val String.cstr: CValuesRef<ByteRef>`.
To get the pointer, `.cstr` should be allocated in native memory, e.g. To get the pointer, `.cstr` should be allocated in native memory, e.g.
<div class="sample" markdown="1" theme="idea" data-highlight-only>
``` ```
val cString = kotlinString.cstr.getPointer(nativeHeap) val cString = kotlinString.cstr.getPointer(nativeHeap)
``` ```
</div>
In all cases the C string is supposed to be encoded as UTF-8. In all cases the C string is supposed to be encoded as UTF-8.
To skip automatic conversion and ensure raw pointers are used in the bindings `noStringConversion` To skip automatic conversion and ensure raw pointers are used in the bindings `noStringConversion`
statement in `.def` file could be used, i.e. statement in `.def` file could be used, i.e.
<div class="sample" markdown="1" theme="idea" mode="c">
```c
noStringConversion = LoadCursorA LoadCursorW
``` ```
noStringConversion = LoadCursorA LoadCursorW
``` </div>
This way any value of type `CPointer<ByteVar>` could be passed as an argument of `const char*` type. This way any value of type `CPointer<ByteVar>` could be passed as an argument of `const char*` type.
If Kotlin string shall me passed code like that could be used: If Kotlin string shall me passed code like that could be used:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin ```kotlin
memScoped { memScoped {
LoadCursorA(null, "cursor.bmp".cstr.ptr) // for ASCII version LoadCursorA(null, "cursor.bmp".cstr.ptr) // for ASCII version
@@ -354,12 +481,17 @@ memScoped {
} }
``` ```
</div>
### Scope-local pointers ### ### Scope-local pointers ###
It is possible to create scope-stable pointer of C representation of `CValues<T>` It is possible to create scope-stable pointer of C representation of `CValues<T>`
instance using `CValues<T>.ptr` extension property available under `memScoped { ... }`. instance using `CValues<T>.ptr` extension property available under `memScoped { ... }`.
It allows to use APIs which requires C pointers with lifetime bound to certain `MemScope`. For example: It allows to use APIs which requires C pointers with lifetime bound to certain `MemScope`. For example:
```
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
memScoped { memScoped {
items = arrayOfNulls<CPointer<ITEM>?>(6) items = arrayOfNulls<CPointer<ITEM>?>(6)
arrayOf("one", "two").forEachIndexed { index, value -> items[index] = value.cstr.ptr } arrayOf("one", "two").forEachIndexed { index, value -> items[index] = value.cstr.ptr }
@@ -367,6 +499,9 @@ memScoped {
... ...
} }
``` ```
</div>
In this example all values passed to the C API `new_menu()` have lifetime of innermost `memScope` 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. it belongs to. Once control flow will leave `memScoped` scope C pointers become invalid.
@@ -388,10 +523,16 @@ methods available:
`CValue<T>` to the memory, and then runs the passed lambda with this placed `CValue<T>` to the memory, and then runs the passed lambda with this placed
value `T` as receiver. So to read a single field, the following code can be value `T` as receiver. So to read a single field, the following code can be
used: used:
```
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val fieldValue = structValue.useContents { field } val fieldValue = structValue.useContents { field }
``` ```
</div>
### Callbacks ### ### Callbacks ###
To convert Kotlin function to pointer to C function, To convert Kotlin function to pointer to C function,
@@ -417,28 +558,43 @@ the callback itself, to safely swim from Kotlin to Kotlin through the C world.
Such wrapping is possible with `StableRef` class. Such wrapping is possible with `StableRef` class.
To wrap the reference: To wrap the reference:
```
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val stablePtr = StableRef.create(kotlinReference) val stablePtr = StableRef.create(kotlinReference)
val voidPtr = stablePtr.value val voidPtr = stablePtr.value
``` ```
</div>
where the `voidPtr` is `COpaquePointer` and can be passed to the C function. where the `voidPtr` is `COpaquePointer` and can be passed to the C function.
To unwrap the reference: To unwrap the reference:
``` <div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val stablePtr = StableRef.fromValue(voidPtr) val stablePtr = StableRef.fromValue(voidPtr)
val kotlinReference = stablePtr.get() val kotlinReference = stablePtr.get()
``` ```
</div>
where `kotlinReference` is the original wrapped reference (however it's type is where `kotlinReference` is the original wrapped reference (however it's type is
`Any` so it may require casting). `Any` so it may require casting).
The created `StableRef` should eventually be manually disposed using The created `StableRef` should eventually be manually disposed using
`.dispose()` method to prevent memory leaks: `.dispose()` method to prevent memory leaks:
``` <div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
stablePtr.dispose() stablePtr.dispose()
``` ```
</div>
After that it becomes invalid, so `voidPtr` can't be unwrapped anymore. After that it becomes invalid, so `voidPtr` can't be unwrapped anymore.
See `samples/libcurl` for more details. See `samples/libcurl` for more details.
@@ -451,7 +607,9 @@ wrapping with supported declarations. E.g. function-like macro `FOO` can be
exposed as function `foo` by exposed as function `foo` by
[adding the custom declaration](#adding-custom-declarations) to the library: [adding the custom declaration](#adding-custom-declarations) to the library:
``` <div class="sample" markdown="1" theme="idea" mode="c">
```c
headers = library/base.h headers = library/base.h
--- ---
@@ -461,6 +619,8 @@ static inline int foo(int arg) {
} }
``` ```
</div>
### Definition file hints ### ### Definition file hints ###
The `.def` file supports several options for adjusting generated bindings. The `.def` file supports several options for adjusting generated bindings.
@@ -484,9 +644,12 @@ neither implicit integer casts nor C-style integer casts (e.g.
`(size_t) intValue`), so to make writing portable code in such cases easier, `(size_t) intValue`), so to make writing portable code in such cases easier,
`convert` method is provided: `convert` method is provided:
``` <div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
fun ${type1}.convert<${type2}>(): ${type2} fun ${type1}.convert<${type2}>(): ${type2}
``` ```
</div>
where each of `type1` and `type2` must be an integral type, either signed or unsigned. where each of `type1` and `type2` must be an integral type, either signed or unsigned.
@@ -497,11 +660,15 @@ methods, depending on `type`.
The example of using `convert`: The example of using `convert`:
``` <div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
fun zeroMemory(buffer: COpaquePointer, size: Int) { fun zeroMemory(buffer: COpaquePointer, size: Int) {
memset(buffer, 0, size.convert<size_t>()) memset(buffer, 0, size.convert<size_t>())
} }
``` ```
</div>
Also the type parameter can be inferred automatically and thus may be omitted Also the type parameter can be inferred automatically and thus may be omitted
in some cases. in some cases.