From 51faa779f685f6149a0e53d3d585d3533ba2d435 Mon Sep 17 00:00:00 2001 From: Watson David <30525704+B1ggDave@users.noreply.github.com> Date: Thu, 13 Sep 2018 15:15:05 +0200 Subject: [PATCH] proofread (#2058) --- INTEROP.md | 240 ++++++++++++++++++++++++++--------------------------- 1 file changed, 120 insertions(+), 120 deletions(-) diff --git a/INTEROP.md b/INTEROP.md index 4387edcc6ea..51638c4c15d 100644 --- a/INTEROP.md +++ b/INTEROP.md @@ -2,27 +2,27 @@ ## Introduction ## - _Kotlin/Native_ follows general tradition of Kotlin to provide excellent -existing platform software interoperability. In case of native platform -most important interoperability target is a C library. Thus _Kotlin/Native_ -comes with an `cinterop` tool, which could be used to quickly generate + _Kotlin/Native_ follows the general tradition of Kotlin to provide excellent +existing platform software interoperability. In the case of a native platform, +the most important interoperability target is a C library. So _Kotlin/Native_ +comes with a `cinterop` tool, which can be used to quickly generate everything needed to interact with an external library. - Following workflow is expected when interacting with the native library. - * create `.def` file describing what to include into bindings - * use `cinterop` tool to produce Kotlin bindings + The following workflow is expected when interacting with the native library. + * create a `.def` file describing what to include into bindings + * use the `cinterop` tool to produce Kotlin bindings * run _Kotlin/Native_ compiler on an application to produce the final executable - Interoperability tool analyses C headers and produces "natural" mapping of -types, function and constants into the Kotlin world. Generated stubs can be -imported into an IDE for purposes of code completion and navigation. + The interoperability tool analyses C headers and produces a "natural" mapping of +the types, functions, and constants into the Kotlin world. The generated stubs can be +imported into an IDE for the purpose of code completion and navigation. - Interoperability with Swift/Objective-C is provided too and covered by the + Interoperability with Swift/Objective-C is provided too and covered in a separate document [OBJC_INTEROP.md](OBJC_INTEROP.md). ## Simple example ## -Build the dependencies and the compiler (see `README.md`). +Build the dependencies and compiler (see `README.md`). Prepare stubs for the system sockets library: @@ -49,8 +49,8 @@ Compile the echo server: -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 +This whole process is automated in the `build.sh` script, which also supports cross-compilation +to supported cross-targets with `TARGET=raspberrypi ./build.sh` (the `cross_dist` target must be executed first). Run the server: @@ -73,12 +73,12 @@ telnet localhost 3000 -Write something to console and watch server echoing it back. +Write something to the console and watch the server echo it back. ## Creating bindings for a new library ## - To create bindings for a new library, start by creating `.def` file. -Structurally it's a simple property file, looking like this: + To create bindings for a new library, start by creating a `.def` file. +Structurally it's a simple property file, which looks like this:
@@ -90,8 +90,8 @@ 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): +Then run the `cinterop` tool with something like this (note that for host libraries that are not included +in the sysroot search paths, headers may be needed):
@@ -102,41 +102,41 @@ cinterop -def zlib.def -copt -I/opt/local/include -o zlib
-This command will produce `zlib.klib` compiled library and +This command will produce a `zlib.klib` compiled library and `zlib-build/kotlin` directory containing Kotlin source code for the library. -If behavior for certain platform shall be modified, one may use format like +If the behavior for a certain platform needs to be modified, you can use a format like `compilerOpts.osx` or `compilerOpts.linux` to provide platform-specific values -to options. +to the options. -Note, that generated bindings are generally platform-specific, so if developing for -multiple targets, bindings need to be regenerated. +Note, that the generated bindings are generally platform-specific, so if you are developing for +multiple targets, the bindings need to be regenerated. -After generation of bindings they could be used by IDE as proxy view of the +After the generation of bindings, they can be used by the IDE as a proxy view of the native library. -For typical Unix library with config script `compilerOpts` will likely contain -output of config script with `--cflags` flag (maybe without exact paths). +For a typical Unix library with a config script, the `compilerOpts` will likely contain +the output of a config script with the `--cflags` flag (maybe without exact paths). -Output of config script with `--libs` shall be passed as `-linkedArgs` `kotlinc` +The output of a config script with `--libs` will be passed as a `-linkedArgs` `kotlinc` flag value (quoted) when compiling. ### Selecting library headers -When library headers are imported to C program with `#include` directive, -all of the headers included by these headers are also included to the program. -Thus all header dependencies are included in generated stubs as well. +When library headers are imported to a C program with the `#include` directive, +all of the headers included by these headers are also included in the program. +So all header dependencies are included in generated stubs as well. -This behaviour is correct but may be very inconvenient for some libraries. So -it is possible to specify in `.def` file which of the included headers are to -be imported. The separate declarations from other headers may also be imported +This behavior is correct but it can be very inconvenient for some libraries. So +it is possible to specify in the `.def` file which of the included headers are to +be imported. The separate declarations from other headers can also be imported in case of direct dependencies. #### Filtering headers by globs -It is possible to filter header by globs. The `headerFilter` property value -from the `.def` file is treated as space-separated list of globs. If the -included header matches any of the globs, then declarations from this header +It is possible to filter headers by globs. The `headerFilter` property value +from the `.def` file is treated as a space-separated list of globs. If the +included header matches any of the globs, then the declarations from this header are included into the bindings. The globs are applied to the header paths relative to the appropriate include @@ -152,16 +152,16 @@ headerFilter = SomeLibrary/** -If `headerFilter` is not specified, then all headers are included. +If a `headerFilter` is not specified, then all headers are included. #### Filtering by module maps -Some libraries have proper `module.modulemap` or `module.map` files among its +Some libraries have proper `module.modulemap` or `module.map` files in its headers. For example, macOS and iOS system libraries and frameworks do. The [module map file](https://clang.llvm.org/docs/Modules.html#module-map-language) 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 +can be filtered out using the experimental `excludeDependentModules` option of the `.def` file:
@@ -176,14 +176,14 @@ excludeDependentModules = true When both `excludeDependentModules` and `headerFilter` are used, they are -applied as intersection. +applied as an intersection. ### Adding custom declarations ### Sometimes it is required to add custom C declarations to the library before -generating bindings (e.g. for [macros](#macros)). Instead of creating +generating bindings (e.g., for [macros](#macros)). Instead of creating an 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 a separating line, containing only the separator sequence `---`:
@@ -201,13 +201,13 @@ 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. +functions with the body should be declared as `static`. +The declarations are parsed after including the files from the `headers` list. ### Including static library in your klib Sometimes it is more convenient to ship a static library with your product, -rather that assuming it is available within the user environment. +rather than assume it is available within the user's environment. To include a static library into `.klib` use `staticLibrary` and `libraryPaths` clauses. For example: @@ -221,48 +221,48 @@ 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 +`/opt/local/lib` and `/usr/local/opt/curl/lib`, and if it is found include the library binary into `klib`. -When using such `klib` in your program the library is linked automatically. +When using such `klib` in your program, the library is linked automatically. ## Using bindings ## ### Basic interop types ### -All supported C types have corresponding representations in Kotlin: +All the supported C types have corresponding representations in Kotlin: -* Signed, unsigned integral and floating point types are mapped to their +* Signed, unsigned integral, and floating point types are mapped to their Kotlin counterpart with the same width. * Pointers and arrays are mapped to `CPointer?`. * Enums can be mapped to either Kotlin enum or integral values, depending on - heuristics and definition file hints (see "Definition file hints" below). -* Structs are mapped to types having fields available via dot notation, + heuristics and the definition file hints (see "Definition file hints" below). +* Structs are mapped to types having fields available via the dot notation, i.e. `someStructInstance.field1`. -* `typedef`s are represented as `typealias`es. +* `typedef` are represented as `typealias`. -Also any C type has the Kotlin type representing the lvalue of this type, -i.e. the value located in memory rather than simple immutable self-contained -value. Think C++ references, as similar concept. +Also, any C type has the Kotlin type representing the lvalue of this type, +i.e., the value located in memory rather than a simple immutable self-contained +value. Think C++ references, as a similar concept. For structs (and `typedef`s to structs) this representation is the main one and has the same name as the struct itself, for Kotlin enums it is named `${type}Var`, for `CPointer` it is `CPointerVar`, and for most other types it is `${type}Var`. -For those types that have both representations, the "lvalue" one has mutable -`.value` property for accessing value. +For types that have both representations, the one with a "lvalue" has a mutable +`.value` property for accessing the value. #### Pointer types #### The type argument `T` of `CPointer` must be one of the "lvalue" types -described above, e.g. the C type `struct S*` is mapped to `CPointer`, +described above, e.g., the C type `struct S*` is mapped to `CPointer`, `int8_t*` is mapped to `CPointer`, and `char**` is mapped to `CPointer>`. 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: +type support all the Kotlin operations related to handling `null`, e.g. `?:`, `?.`, +`!!` etc.:
@@ -272,7 +272,7 @@ val path = getenv("PATH")?.toKString() ?: ""
-Since the arrays are also mapped to `CPointer`, it supports `[]` operator +Since the arrays are also mapped to `CPointer`, it supports the `[]` operator for accessing values by index:
@@ -295,7 +295,7 @@ and returns the pointer to it. supertype for any other pointer type. So if the C function takes `void*`, then the Kotlin binding accepts any `CPointer`. -Casting any pointer (including `COpaquePointer`) can be done with +Casting a pointer (including `COpaquePointer`) can be done with `.reinterpret`, e.g.:
@@ -316,11 +316,11 @@ val intPtr: CPointer = bytePtr.reinterpret()
-As in C, those reinterpret casts are unsafe and could potentially lead to -subtle memory problems in an application. +As is with C, these reinterpret casts are unsafe and can potentially lead to +subtle memory problems in the application. Also there are unsafe casts between `CPointer?` and `Long` available, -provided by `.toLong()` and `.toCPointer()` extension methods: +provided by the `.toLong()` and `.toCPointer()` extension methods:
@@ -332,11 +332,11 @@ 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. +can be omitted as usual due to the type inference. ### Memory allocation ### -The native memory can be allocated using `NativePlacement` interface, e.g. +The native memory can be allocated using the `NativePlacement` interface, e.g.
@@ -356,8 +356,8 @@ val bytePtr = placement.allocArray(5):
-The most "natural" placement is object `nativeHeap`. -It corresponds to allocating native memory with `malloc` and provides additional +The most "natural" placement is in the object `nativeHeap`. +It corresponds to allocating native memory with `malloc` and provides an additional `.free()` operation to free allocated memory:
@@ -370,9 +370,9 @@ nativeHeap.free(buffer)
-However the lifetime of allocated memory is often bound to lexical scope. +However, the lifetime of allocated memory is often bound to the lexical scope. 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 an implicit receiver, so it is possible to allocate native memory with `alloc` and `allocArray`, and the allocated memory will be automatically freed after leaving the scope. @@ -393,11 +393,11 @@ val fileSize = memScoped { ### Passing pointers to bindings ### -Although C pointers are mapped to `CPointer` type, the C function +Although C pointers are mapped to the `CPointer` type, the C function pointer-typed parameters are mapped to `CValuesRef`. When passing -`CPointer` as the value of such parameter, it is passed to C function as is. -However, the sequence of values can be passed instead of pointer. In this case -the sequence is passed "by value", i.e. the C function receives the pointer to +`CPointer` as the value of such a parameter, it is passed to the C function as is. +However, the sequence of values can be passed instead of a pointer. In this case +the sequence is passed "by value", i.e., the C function receives the pointer to the temporary copy of that sequence, which is valid only until the function returns. The `CValuesRef` representation of pointer parameters is designed to support @@ -407,7 +407,7 @@ methods are provided: * `${type}Array.toCValues()`, where `type` is the Kotlin primitive type * `Array?>.toCValues()`, `List?>.toCValues()` -* `cValuesOf(vararg elements: ${type})`, where `type` is primitive or pointer +* `cValuesOf(vararg elements: ${type})`, where `type` is a primitive or pointer For example: @@ -437,10 +437,10 @@ foo(cValuesOf(1, 2, 3), 3) ### Working with the strings ### Unlike other pointers, the parameters of type `const char*` are represented as -Kotlin `String`. So it is possible to pass any Kotlin string to the binding -expecting C string. +a Kotlin `String`. So it is possible to pass any Kotlin string to a binding +expecting a C string. -There are also available some tools to convert between Kotlin and C strings +There are also some tools available to convert between Kotlin and C strings manually: * `fun CPointer.toKString(): String` @@ -456,10 +456,10 @@ manually:
-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` -statement in `.def` file could be used, i.e. +To skip automatic conversion and ensure raw pointers are used in the bindings, a `noStringConversion` +statement in the `.def` file could be used, i.e.
@@ -469,8 +469,8 @@ 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: +This way any value of type `CPointer` can be passed as an argument of `const char*` type. +If a Kotlin string should be passed, code like this could be used:
@@ -485,9 +485,9 @@ 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: +It is possible to create a scope-stable pointer of C representation of `CValues` +instance using the `CValues.ptr` extension property, available under `memScoped { ... }`. +It allows using the APIs which require C pointers with a lifetime bound to a certain `MemScope`. For example:
@@ -502,25 +502,25 @@ 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. +In this example, all values passed to the C API `new_menu()` have a lifetime of the innermost `memScope` +it belongs to. Once the control flow leaves the `memScoped` scope the C pointers become invalid. ### Passing and receiving structs by value ### -When C function takes or returns a struct `T` by value, the corresponding +When a C function takes or returns a struct `T` by value, the corresponding argument type or return type is represented as `CValue`. -`CValue` is an opaque type, so structure fields cannot be accessed with -appropriate Kotlin properties. It could be acceptable, if API uses structures -as handles, but if field access is required, there are following conversion +`CValue` is an opaque type, so the structure fields cannot be accessed with +the appropriate Kotlin properties. It should be possible, if an API uses structures +as handles, but if field access is required, there are the following conversion methods available: -* `fun T.readValue(): CValue`. Converts (the lvalue) `T` to `CValue`. - So to construct the `CValue`, `T` can be allocated, filled and then +* `fun T.readValue(): CValue`. Converts (the lvalue) `T` to a `CValue`. + So to construct the `CValue`, `T` can be allocated, filled, and then converted to `CValue`. * `CValue.useContents(block: T.() -> R): R`. Temporarily places the - `CValue` to the memory, and then runs the passed lambda with this placed + `CValue` to 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: @@ -535,25 +535,25 @@ methods available: ### Callbacks ### -To convert Kotlin function to pointer to C function, -`staticCFunction(::kotlinFunction)` can be used. It is also allowed to provide -the lambda instead of function reference. The function or lambda must not +To convert a Kotlin function to a pointer to a C function, +`staticCFunction(::kotlinFunction)` can be used. It is also able to provide +the lambda instead of a function reference. The function or lambda must not capture any values. Note that some function types are not supported currently. For example, -it is not possible to get pointer to function that receives or returns structs +it is not possible to get a pointer to a function that receives or returns structs by value. -If the callback doesn't run in the main thread it is mandatory to init the _Kotlin/Native_ +If the callback doesn't run in the main thread, it is mandatory to init the _Kotlin/Native_ runtime by calling `kotlin.native.initRuntimeIfNeeded()`. #### Passing user data to callbacks #### Often C APIs allow passing some user data to callbacks. Such data is usually -provided by user when configuring the callback. It is passed to some C function +provided by the user when configuring the callback. It is passed to some C function (or written to the struct) as e.g. `void*`. -However references to Kotlin objects can't be directly passed to C. -So they require wrapping before configuring callback and then unwrapping in +However, references to Kotlin objects can't be directly passed to C. +So they require wrapping before configuring the callback and then unwrapping in the callback itself, to safely swim from Kotlin to Kotlin through the C world. Such wrapping is possible with `StableRef` class. @@ -568,7 +568,7 @@ val voidPtr = stablePtr.value
-where the `voidPtr` is `COpaquePointer` and can be passed to the C function. +where the `voidPtr` is a `COpaquePointer` and can be passed to the C function. To unwrap the reference: @@ -581,11 +581,11 @@ val kotlinReference = stablePtr.get() -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). The created `StableRef` should eventually be manually disposed using -`.dispose()` method to prevent memory leaks: +the `.dispose()` method to prevent memory leaks:
@@ -597,13 +597,13 @@ stablePtr.dispose() After that it becomes invalid, so `voidPtr` can't be unwrapped anymore. -See `samples/libcurl` for more details. +See the `samples/libcurl` for more details. ### Macros ### -Every C macro that expands to a constant is represented as Kotlin property. -Other macros are not supported. However they can be exposed manually by -wrapping with supported declarations. E.g. function-like macro `FOO` can be +Every C macro that expands to a constant is represented as a Kotlin property. +Other macros are not supported. However, they can be exposed manually by +wrapping them 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: @@ -623,26 +623,26 @@ static inline int foo(int arg) { ### Definition file hints ### -The `.def` file supports several options for adjusting generated bindings. +The `.def` file supports several options for adjusting the generated bindings. -* `excludedFunctions` property value specifies a space-separated list of names +* `excludedFunctions` property value specifies a space-separated list of the names of functions that should be ignored. This may be required because a function - declared in C header is not generally guaranteed to be really callable, and + declared in the C header is not generally guaranteed to be really callable, and it is often hard or impossible to figure this out automatically. This option can also be used to workaround a bug in the interop itself. * `strictEnums` and `nonStrictEnums` properties values are space-separated - lists of the enums that should be generated as Kotlin enum or as integral + lists of the enums that should be generated as a Kotlin enum or as integral values correspondingly. If the enum is not included into any of these lists, - than it is generated according to the heuristics. + then it is generated according to the heuristics. ### Portability ### -Sometimes the C libraries have function parameters or struct fields of +Sometimes the C libraries have function parameters or struct fields of a platform-dependent type, e.g. `long` or `size_t`. Kotlin itself doesn't provide 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: +the `convert` method is provided:
@@ -670,5 +670,5 @@ fun zeroMemory(buffer: COpaquePointer, size: Int) {
-Also the type parameter can be inferred automatically and thus may be omitted +Also, the type parameter can be inferred automatically and so may be omitted in some cases.