Best practice for passing Array of Objects from C++ to TS using FFI
Hey, what is the best way to pass an array of objects from C++ via DLL to TS?
We are trying to write a Serial Library and we query a list of available ports and their information. Similar to the serialport.io node library. What would be the best way to pass all the available ports and their information to TS?
4 Replies
Maybe you can find an example here https://github.com/aapoalas/libclang_deno
If it's the Deno/JS side doing the querying ("do you have available ports, and if so then which and how many?") and the C++/DLL side answering the question ("thank you for the question, I have 3 open ports; A, B, and C") then the way would usually be an API like this:
1. a native API that either takes a
*uintptr_t
and returns *OpenPort
, or takes an **OpenPort
and returns a uintptr_t
.
2. a native API that takes an *OpenPort
and uintptr_t
, and deallocates the list of open ports.
3. any APIs you need for working with OpenPort
objects.
When the Deno/JS side asks the question, it'll pass the pointer as a parameter. The native side writes data through that pointer and returns the rest of the answer; this way you'd get the pointer to the first OpenPort
in the list, and the number of OpenPort
objects in the list. Then on Deno-side you just perform memory-offsets to find the rest of the objects in the list.
Alternatively, you can wrap the list into an object itself. Your API would then be something like extern void getOpenPorts(*OpenPortsList)
; JS side allocates enough room to hold an OpenPortsList
using an ArrayBuffer and passes the pointer to that to the DLL. The DLL then writes the necessary data through that pointer (the actual list pointer and length, probably).
Besides that you'd still obviously need a destructor for OpenPortsList
, and then all the public API functions for working with OpenPortsList
, like OpenPortsList::getOpenPort(index)
kind of things.
In libclang_deno, you can find out-pointers like *uintptr_t
and **OpenPort
by searching for OUT
in mod.ts
; since the out-pointers are only ever needed temporarily for a single call, I reuse the same allocation all-over the place there. Hence, it's easy to see when it gets used.
eg. this method sends an out-pointer OUT
to getFileContents
; the DLL method returns a pointer to the file contents char buffer and writes the length of the buffer into the OUT
pointer; the result is read using the OUT_64
BigUint64Array
view of the OUT
memory.
Naturally, a char buffer and an array of objects feel very far away from one another, but conceptually you can think of a char buffer as just an array of objects where the object type is char
.You may also be interested in my txx library built ontop of libclang_deno: it's not a very complete system as I kind of gave up on trying to understand C++ templates well enough to automatically translate eg.
std::string
and std::function
into Deno FFI APIs, but it might be a pretty capable thing for auto-generating bindings and Deno-side interface classes for working with C++ FFI classes.GitHub
GitHub - aapoalas/txx: Unsafe interop between Deno FFI and C++
Unsafe interop between Deno FFI and C++. Contribute to aapoalas/txx development by creating an account on GitHub.
If you just want a serial library, checkout the one recently posted by lucsoft in #showcase (rust based web serial). I've also got my own pure deno one https://github.com/jeff-hykin/deno_serial , but I need to refine the API and clean up before doing a 1.0 release
GitHub
GitHub - jeff-hykin/deno_serial: Serialport API for Deno.
Serialport API for Deno. Contribute to jeff-hykin/deno_serial development by creating an account on GitHub.