kotlinkotlin-nativekotlin-interop

Dealing with (U)Int in Kotlin Native C interop


I'm trying to use the PiGPIO library with Kotlin Native as a linked library (not using the deamon). So I'm using C interop with a .def file that references the pigpio.h file.

It works (I managed to get a LED blinking) but there is an issue with the typing of integers. Althoug I didn't enable the experimental unsigned integers feature, the generated stubs are using type UInt.

For example for the parameters of this function:

@kotlinx.cinterop.internal.CCall public external fun gpioSetMode(gpio: kotlin.UInt, mode: kotlin.UInt): kotlin.Int { /* compiled code */ }

That's OK with me as they are of type unsigned in C and I want this to be as type-safe as possible:

int gpioSetMode(unsigned gpio, unsigned mode);

Now the problem is that the values to be used as parameters for the functions are defined using macro definitions in the .h file. For example for the mode parameter:

#define PI_INPUT  0
#define PI_OUTPUT 1

The generated Kotlin constants corresponding to those values are of type Int:

public const val PI_INPUT: kotlin.Int /* compiled code */
public const val PI_OUTPUT: kotlin.Int /* compiled code */

However, although calling the function with the constant as a parameter is possible:

gpioSetMode(14, PI_OUTPUT) // compiles fine

I can't create a method that takes the mode as a parameter and use it:

fun main() {
    setMode(PI_OUTPUT) // fails to compile (Type Mismatch)
}

fun setMode(mode : UInt) {
    gpioSetMode(14, mode)
}

Is there a way to force all constants of positive integers to be of type UInt ?


Solution

  • AFAIK, there is no such option in the cinterop tool.
    In fact, one can say that the problem grows from the library header not using unsigned literals in it's "define" section. But it can be omitted in C, so this header is fine. The tool here is a bit nerdier, so it assumes all integer literals with no additional suffix as the signed typed.
    About the way that your generated function works. In Kotlin, there is a smart cast concept(see here), but here it is a problem. In this documentation part, there is a note on smart-casting availability only for checks inside a module. In your case, gpioSetMode(gpio, mode) and PI_OUTPUT are located in the same module, while your setMode is in another one.That's why the first call compiles and the second one does not.
    I managed to workaround it in my small sample like that: just added into my code this like, redefining the constant

    import my.*
    const val PI_OUTPUT = my.PI_OUTPUT
    

    where my is the library package, most probably pigpio for you. After that, smart casts will be available for the library functions, and all functions you declare in this module.