I'm trying to use a C++ library (Jolt physics engi...
# random
a
I'm trying to use a C++ library (Jolt physics engine) in Kotlin Native. Because KN can only interop with C, that means I have to write
extern "C"
wrappers for the C++ library. But I am very stuck on a problem, because I am confused by C++ scoping/pointers/references/etc. Help would be very appreciated! I'm stuck trying to call a static function: Quat:sIdentity() Here's the wrapper function I've written:
Copy code
struct JoltC_Quat { void *obj; };
typedef struct JoltC_Quat JoltC_Quat_t;

JoltC_Quat_t * JoltC_Quat_sIdentity() {
  const Quat& resultValue = Quat::sIdentity();
  JoltC_Quat_t* result = new JoltC_Quat_t();
  result->obj = const_cast<void*>(reinterpret_cast<const void*>(&resultValue));
  return result;
}
Which is correctly recognised by KN, and I've generated cinterop for it. However, when I call it and print the values, I expect to see
[0,0,0,1]
, but why does it print
[2.3234582E-38, 1.4E-45, 8.3204753E-38, 3.4438E-41]
? I had this working a few days ago! I'm pretty sure I haven't changed anything!
Here's the Kotlin Native code:
Copy code
import com.jrouwe.jolt.cinterop.internal.*
import kotlinx.cinterop.*

fun main() {
  val qsi = JoltC_Quat_sIdentity()
    ?: error("null qsi")
  println("qsi: $qsi")
  println("qsi: ${qsi.pointed}")
  println("qsi: ${qsi.coordsString()}")
}

private fun CPointer<JoltC_Quat>.coordsString(): String =
  buildString {
    append("[")
    append(JoltC_Quat_GetX(this@coordsString))
    append(", ")
    append(JoltC_Quat_GetY(this@coordsString))
    append(", ")
    append(JoltC_Quat_GetZ(this@coordsString))
    append(", ")
    append(JoltC_Quat_GetW(this@coordsString))
    append("]")
  }
Which prints
Copy code
qsi: CPointer(raw=0x600001e28140)
qsi: kotlinx.cinterop.NativePointed@e10218
qsi: [2.3234582E-38, 1.4E-45, 8.3204753E-38, 3.4438E-41]
All of the GetX/Y/Z/W C++ functions look like this:
Copy code
float JoltC_Quat_GetX(
  JoltC_Quat_t * self
) {
  Quat * selfCpp = static_cast<Quat *>(self->obj);
  return selfCpp->GetX();
}
e
Copy code
&resultValue
that's definitely wrong, it's a dangling pointer after the function returns
a
thanks! I think I see what you mean...
So I guess it worked by chance before, that coincidentally
&resultValue
pointed to the actual address, but when I run it again the address has been overwritten by something else
Ahhh I was looking at the wrong history! This function works:
Copy code
JoltC_Quat_t * JoltC_Quat_sIdentity() {
  Quat * resultPtr = new Quat();
  *resultPtr = Quat::sIdentity();
  JoltC_Quat_t * result = new JoltC_Quat_t();
  result->obj = reinterpret_cast<void *>(resultPtr);
  return result;
}
k
Now this looks like a memory leak, as you're not freeing
resultPtr
. (And I'm not suggesting you should free it in this function, as you would then end up with a dangling pointer again.)
a
yeah that's right - I'll have to free it later
k
But why use a pointer to a pointer in the first place? With a change in
GetX
etc you can make
self.obj
return the object instead of a pointer to the object and then you won't have to free it.
a
for other C libraries I use
memScope.defer {  CThing_Destroy(foo) }
I would be happy to learn if there's a better way! I've mostly been following the principles in guides like https://nachtimwald.com/2017/08/18/wrapping-c-objects-in-c/, but no guide seems to cover all situations
btw if you want to look at all the bindings, they're available here: https://github.com/aSemy/JoltC/blob/f5851eb7bfe8a91ddbcc5199dd82086fc2d7703c/src/JoltC/JoltC_Quat.cpp Maybe the context helps