`When calling variadic C functions spread operator...
# kotlin-native
m
When calling variadic C functions spread operator is supported only for *arrayOf(...)
How can I avoid this? Can I write a wrapper function in the .def or something? I already tried
*arrayOf(*x.map { ... }.toTypedArray())
but that was also invalid?
r
As I understand it when you call a variadic C function the varargs are baked in at compile time - so it doesn't take an array at runtime, so you can't call it with a kotlin array. I think you need to find an equivalent function that actually takes an array. I went round the houses with this over the
platform.posix.*
functions for launching a process...
m
There's an equivalent function that takes a va_list instead, would that work somehow?
r
Dunno! I'm a rank noob to both kotlin-native and C / posix... I've managed to stay pointer free my entire life until now... In my case I found a function that took a
CValuesRef<CPointerVar<ByteVar>>
, which I created as so:
Copy code
import kotlinx.cinterop.ByteVar
import kotlinx.cinterop.CPointerVar
import kotlinx.cinterop.CValuesRef
import kotlinx.cinterop.cstr
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.toCValues

memScoped {
  val args: CValuesRef<CPointerVar<ByteVar>> = listOf("a", "b", "c")
    .map { it.cstr.ptr }
    .toCValues().ptr
  // call c function here
}
I've also managed to stay manual memory management free until now, so I'm offering no guarantees as to what happens when that
memscoped
block terminates... since we're passing a pointer to the actual strings / array, I guess the c function had better have finished using them (i.e. it had better be single threaded) or they will be gone. In my case I'm forking a process... I presume that the strings will be copied into the new process's memory space, otherwise nothing would work?!
Oh, I had to append null to the
CValues
as well to signal it had ended...
Though playing with it
kotlinx.cinterop.toCStringArray
might save me a lot of that!
👍 1
m
Luckily the Python C API is easy to read for me, but the internals seem to always use va_list...
j
CPointerVar<COpaquePointer?> ought to give you indexing into the pointers
@martmists what is the c prototype ?
m
The prototype is
int *PyArg_ParseTupleAndKeywords*(PyObject *_args_, PyObject *_kw_, _const_ char *_format_, char *_keywords_[], ...)
j
@kotlinx.cinterop.internal.CCall public external fun PyArg_ParseTupleAndKeywords(args: kotlinx.cinterop.CValuesRef<*>?, kw: kotlinx.cinterop.CValuesRef<*>?, @kotlinx.cinterop.internal.CCall.CString format: kotlin.String?, keywords: kotlinx.cinterop.CValuesRef<kotlinx.cinterop.CPointerVar<kotlinx.cinterop.ByteVar /* = kotlinx.cinterop.ByteVarOf<kotlin.Byte> /> / = kotlinx.cinterop.CPointerVarOf<kotlinx.cinterop.CPointer<kotlinx.cinterop.ByteVar /* = kotlinx.cinterop.ByteVarOf<kotlin.Byte> */>> />?, vararg variadicArguments: kotlin.Any?): kotlin.Int { / compiled code */ } lol. cinterop does a big punt on the elipses. fprintf has the same. you could use fprintf as a soft-run for the whole PyObject calls.
*listOf(outerArgs).toTypedArray<out Any?>()
is as good a shot as any, plus or minus StableRef.create()