diff --git a/INTEROP.md b/INTEROP.md
index dd71ff65c13..4387edcc6ea 100644
--- a/INTEROP.md
+++ b/INTEROP.md
@@ -26,14 +26,28 @@ Build the dependencies and the compiler (see `README.md`).
Prepare stubs for the system sockets library:
- cd samples/socket
- ../../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
+```
+
+
Compile the echo server:
- ../../dist/bin/kotlinc src/main/kotlin/EchoServer.kt \
- -library sockets -o EchoServer
+
+
+```bash
+../../dist/bin/kotlinc src/main/kotlin/EchoServer.kt \
+ -library sockets -o EchoServer
+```
+
+
+
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
@@ -41,11 +55,23 @@ be executed first).
Run the server:
- ./EchoServer.kexe 3000 &
+
+
+```bash
+./EchoServer.kexe 3000 &
+```
+
+
Test the server by connecting to it, for example with telnet:
- telnet localhost 3000
+
+
+```bash
+telnet localhost 3000
+```
+
+
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.
Structurally it's a simple property file, looking like this:
+
+
+```c
+headers = zlib.h
+compilerOpts = -std=c99
+```
+
+
- headers = zlib.h
- compilerOpts = -std=c99
Then run `cinterop` tool with something like (note that for host libraries not included
in sysroot search paths for headers may be needed):
- cinterop -def zlib.def -copt -I/opt/local/include -o zlib
+
+
+```bash
+cinterop -def zlib.def -copt -I/opt/local/include -o zlib
+```
+
+
+
This command will produce `zlib.klib` compiled library and
`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
included with `#include `, then it would probably be
correct to filter headers with
-```
+
+
+
+```c
headerFilter = SomeLibrary/**
```
+
+
If `headerFilter` is not specified, then all headers are included.
#### 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
can be filtered out using experimental `excludeDependentModules` option of the
`.def` file:
-```
+
+
+
+```c
headers = OpenGL/gl.h OpenGL/glu.h GLUT/glut.h
compilerOpts = -framework OpenGL -framework GLUT
excludeDependentModules = true
```
+
+
+
When both `excludeDependentModules` and `headerFilter` are used, they are
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
separator sequence `---`:
-```
+
+
+```c
headers = errno.h
---
@@ -146,6 +198,8 @@ static inline int getErrno() {
}
```
+
+
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`.
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`
clauses. For example:
-```
+
+
+```c
staticLibraries = libfoo.a
libraryPaths = /opt/local/lib /usr/local/opt/curl/lib
```
+
+
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
library binary into `klib`.
@@ -205,14 +263,21 @@ C null pointer is represented as Kotlin's `null`, and the pointer type
`CPointer` is not nullable, but the `CPointer?` is. The values of this
type support all Kotlin operations related to handling `null`, e.g. `?:`, `?.`,
`!!` etc:
-```
+
+
+
+```kotlin
val path = getenv("PATH")?.toKString() ?: ""
```
+
+
Since the arrays are also mapped to `CPointer`, it supports `[]` operator
for accessing values by index:
-```
+
+
+```kotlin
fun shift(ptr: CPointer, length: Int) {
for (index in 0 .. length - 2) {
ptr[index] = ptr[index + 1]
@@ -220,6 +285,8 @@ fun shift(ptr: CPointer, length: Int) {
}
```
+
+
The `.pointed` property for `CPointer` returns the lvalue of type `T`,
pointed by this pointer. The reverse operation is `.ptr`: it takes the lvalue
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
`.reinterpret`, e.g.:
-```
+
+
+
+```kotlin
val intPtr = bytePtr.reinterpret()
```
+
+
+
or
-```
+
+
+
+```kotlin
val intPtr: CPointer = bytePtr.reinterpret()
```
+
+
As in C, those reinterpret casts are unsafe and could potentially lead to
subtle memory problems in an application.
Also there are unsafe casts between `CPointer?` and `Long` available,
provided by `.toLong()` and `.toCPointer()` extension methods:
-```
+
+
+
+```kotlin
val longValue = ptr.toLong()
val originalPtr = longValue.toCPointer()
```
+
+
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.
### Memory allocation ###
The native memory can be allocated using `NativePlacement` interface, e.g.
-```
+
+
+
+```kotlin
val byteVar = placement.alloc()
```
+
+
+
or
-```
+
+
+
+```kotlin
val bytePtr = placement.allocArray(5):
```
+
+
The most "natural" placement is object `nativeHeap`.
It corresponds to allocating native memory with `malloc` and provides additional
`.free()` operation to free allocated memory:
-```
+
+
+```kotlin
val buffer = nativeHeap.allocArray(size)
+
However the lifetime of allocated memory is often bound to lexical scope.
It is possible to define such scope with `memScoped { ... }`.
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
used like
-```
+
+
+
+```kotlin
val fileSize = memScoped {
val statBuf = alloc()
val error = stat("/", statBuf.ptr)
@@ -288,6 +389,8 @@ val fileSize = memScoped {
}
```
+
+
### Passing pointers to bindings ###
Although C pointers are mapped to `CPointer` type, the C function
@@ -309,18 +412,28 @@ methods are provided:
For example:
C:
-```
+
+
+
+```c
void foo(int* elements, int count);
...
int elements[] = {1, 2, 3};
foo(elements, 3);
```
+
+
Kotlin:
-```
+
+
+
+```kotlin
foo(cValuesOf(1, 2, 3), 3)
```
+
+
### Working with the strings ###
Unlike other pointers, the parameters of type `const char*` are represented as
@@ -334,19 +447,33 @@ manually:
* `val String.cstr: CValuesRef`.
To get the pointer, `.cstr` should be allocated in native memory, e.g.
+
+
+
```
val cString = kotlinString.cstr.getPointer(nativeHeap)
```
+
+
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`
statement in `.def` file could be used, i.e.
+
+
+
+```c
+noStringConversion = LoadCursorA LoadCursorW
```
- noStringConversion = LoadCursorA LoadCursorW
-```
+
+
+
This way any value of type `CPointer` could be passed as an argument of `const char*` type.
If Kotlin string shall me passed code like that could be used:
+
+
+
```kotlin
memScoped {
LoadCursorA(null, "cursor.bmp".cstr.ptr) // for ASCII version
@@ -354,12 +481,17 @@ memScoped {
}
```
+
+
### 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:
-```
+
+
+
+```kotlin
memScoped {
items = arrayOfNulls?>(6)
arrayOf("one", "two").forEachIndexed { index, value -> items[index] = value.cstr.ptr }
@@ -367,6 +499,9 @@ memScoped {
...
}
```
+
+
+
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.
@@ -388,10 +523,16 @@ methods available:
`CValue` 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
used:
- ```
+
+
+
+ ```kotlin
val fieldValue = structValue.useContents { field }
```
+
+
+
### Callbacks ###
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.
To wrap the reference:
-```
+
+
+
+```kotlin
val stablePtr = StableRef.create(kotlinReference)
val voidPtr = stablePtr.value
```
+
+
+
where the `voidPtr` is `COpaquePointer` and can be passed to the C function.
To unwrap the reference:
-```
+
+
+```kotlin
val stablePtr = StableRef.fromValue(voidPtr)
val kotlinReference = stablePtr.get()
```
+
+
+
where `kotlinReference` is the original wrapped reference (however it's type is
`Any` so it may require casting).
The created `StableRef` should eventually be manually disposed using
`.dispose()` method to prevent memory leaks:
-```
+
+
+```kotlin
stablePtr.dispose()
```
+
+
After that it becomes invalid, so `voidPtr` can't be unwrapped anymore.
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
[adding the custom declaration](#adding-custom-declarations) to the library:
-```
+
+
+```c
headers = library/base.h
---
@@ -461,6 +619,8 @@ static inline int foo(int arg) {
}
```
+
+
### Definition file hints ###
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,
`convert` method is provided:
-```
+
+
+```kotlin
fun ${type1}.convert<${type2}>(): ${type2}
```
+
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`:
-```
+
+
+```kotlin
fun zeroMemory(buffer: COpaquePointer, size: Int) {
memset(buffer, 0, size.convert())
}
```
+
+
Also the type parameter can be inferred automatically and thus may be omitted
in some cases.