Kotlin/Native allows to compile to a native application and use system libraries. Sometimes, this can be a little confusing at first, for example with pointers of pointers ...
I stumbled over this type a short while ago:
char**. At the time of writing this article, I'm creating a Koltin/Native application. The application calls the posix method
execvp, which can be used to execute prorgams and has
char** as second parameter. But what is a
char**? Well, C doesn't have strings. Strings are represented as character arrays, which can be declared like this
char myString;, which defnies a character array (string) of size 5. As function parameters (and sometimes also in other places), strings are declared like this
char *myString, which means the pointer, a.k.a. the address, of a character array. It is also possible to declare and instantiate a string in C with
char *myString = "hello world";. Here myString is also a character array.
Now, what is this:
char *x;? This is an array of size 4 that contains pointers to character arrays. So it can be used in the following way:
This code will create four character arrays (s1 to s4) and put their addresses into the array x. In other words, it's an array of strings. It is also something that can be put into a function that requires something of type
char **, because this means "pointer to pointers of characters" which translates to "array of addresses of character arrays".
char** translate to in Kotlin? In Kotlin the corresponding type (at least for execvp) is:
char there is
ByteVar. That's ok, because characters can be represented as numbers.
CPointerVar is a pointer for a specific type (here ByteVar), so there's the character arrays.
CValuesRef is a pointer for C values. In this case the C values are pointers of the form
The actual problem
I hadn't worked with pointers of pointers in Kotlin before (the above type in particular), so the question that took me a while to answer was: How can a
ByteVar be created?
To start from the back: a
ByteArray can be created from a Kotlin
String by calling the String function
encodeToByteArray(). To get the pointer to the byte array, it needs to be pinned, for instance with calling
pin() on it, which creates a
Pinned<ByteArray>. The pinned array has a funtion
addressOf(Int) that can be called with the parameter 0 to get the arrays start address. This can be done with multiple strings, so multiple byte array pointers are available. The final step is to get a pointer of an array of those pointers. This can be achieved by putting the array pointers into a list using
listOf and calling the Kotlin/Native utility function
toCValues() on that list.
For better understanding, here is how the whole process looks in code. This is the Kotlin translation of the previous example:
This code can be optimized, but I chose this way in order to go along with the above text.
The part that took me the most time was to find
toCValues(). So, to summarize this article I can give this hint: When developing with Kotlin/Native it might be advisable to take a look into Types.kt and Utils.kt. They have a lot of useful content.