Help with FFI strings
I've been testing with the deno FFI to see if it's powerful enough to leverage the power of other languages into Deno.
This however has been very unfruitful as FFI documentations seems to be "everywhere" when it comes to types, take for example anything that returns a
char*
. There are very few hints on how is one supposed to handle such returns.
I was working with a function similar to this:
extern char* searchByDinoSpecies(char* dinosaursString, char* species);
You can assume the dinosaurString
to be a JSON string or a YAML string, idk. Whatever your compiled C-grandson language lets you handle
In my search I found this article: https://medium.com/deno-the-complete-reference/calling-c-functions-from-deno-part-2-pass-buffers-ad168a3b6cc7
However, two problems arise from here:
1. You cannot use the pointer
type in parameters: []
when loading my dylib
because I'll get a TypeError: Invalid FFI pointer type, expected null, or External
Reading the official Deno docs, the documentation states:
buffer type accepts TypedArrays as parameter, but it always returns a pointer object or null when used as result type like the pointer type.- Source: https://docs.deno.com/runtime/manual/runtime/ffi_api/ Okay, so maybe everything as a
buffer
should work? Well, both parameters sure work when setting buffer
but another problem arises. The output, either pointer
or buffer
is completely useless.
The previous Medium Post shows something like this:
However, when tried to replicate this, another TypeError arises:
Discord has an awfully lame limit, continuing in the comments (?)Medium
Calling C functions from Deno: Part 2 — Pass buffers
Learn how to call C functions from Deno and pass arbitrary buffers to and fro
8 Replies
[CONTINUE 1]
Then I decided to see how others do it and I ended up in the Bun docs. Which has an interesting take when it comes to strings (not providing link to avoid spamming more links) but goes like this: JavaScript strings and C-like strings are different, and that complicates using strings with native libraries.
OK, so maybe a "CString" like Bun it's what I need.
I found Deno issue #14248 where a Deno release is mentioned, and there, in a codeblock an example of such method is used and it requires yer another UnsafePointerView object, neither
pointer
nor buffer
work for this approach. Which leads me to think, that nothing that returns char*
is remotely usable in the FFI and should return a uint8
instead (?)
https://docs.deno.com/api/deno/~/Deno.UnsafePointerView
Docs also go nowhere when explaining such returned types. Don't get me wrong, this is probably a 99.9% skill issue by me.
So, if returning any pointer of buffer from dylib
is the same as doing nothing. Why is the PointerValue
a returned thing if that
is supposed to return a PointerObject
? Why is that not documented? How can I help with that documentation if I manage to figure this out? Does a PointerValue
instead of a PointerObject
mean i messed up somewhere in the compiled language?
I thought this was possible in the past because some stackoverflow answers point that pointer
is old and work must be done with Uint8Array
if you want to somehow handle the function results. When was this changed? was 1.14 the only release with changes to the FFI?
Apologies in advance if my tone might be interpreted as un-supportive or if the text length is not adequate.Handling strings with Deno's FFI can indeed be a bit tricky, but let's see if we can clarify some of the confusion based on the documentation.
Firstly, when dealing with functions that return a
char*
from a C library, you would typically use the pointer
type in Deno's FFI to represent the returned value. As per the documentation, the buffer
type is used for passing JavaScript TypedArrays as parameters to your FFI functions, and when used as a return type, it will return a pointer object or null.
Here's a step-by-step approach to handle a function like searchByDinoSpecies
:
1. Define the FFI bindings in Deno using Deno.dlopen
with the buffer
type for the input parameters and pointer
for the return type:
2. When calling searchByDinoSpecies
, you need to convert your input strings (JSON or YAML) into TypedArrays (Uint8Array), ensuring they are null-terminated C-style strings:
3. Call the function with these arrays:
4. To handle the returned char*
(which is now a pointer object in Deno), you can use Deno.UnsafePointerView
to read the string:
Regarding the error you encountered:
```typescript
Argument of type 'PointerValue<unknown>' is not assignableto parameter of type 'PointerObject<unknown>'.
Type 'null' is not assignable to type 'PointerObject<unknown>'
``
This seems to suggest that there's a mismatch between the expected types. Ensure that you are using the correct types as per the latest Deno FFI API. If
PointerValue` is being returned, it might be an outdated type or an issue with the type definitions. As of Deno 1.31, pointers are represented as plain objects or null for null pointers.
If you're still encountering issues, it might be helpful to check the exact version of Deno you're using and ensure that your code aligns with the FFI API for that version. Remember that the FFI API is marked as unstable, and there may be changes between versions.
Lastly, if you're still stuck, consider reaching out to the DenoI'm already at that stage. Using text encoders to feed
Uint8Array
into the function buffer
parameters. However, this response troubles me as step 4 is still the exact same type specifically a Deno.PointerValue<unknown>
. Deno still whines about types when doing a new UnsafePointerView
. Trying to change the PointerValue from unknown
to anything else makes the type checker angry as well.I apologize for the confusion. It seems there might be a misunderstanding regarding the usage of
Deno.UnsafePointerView
. Based on your description, you are receiving a Deno.PointerValue<unknown>
which should be a valid argument for creating a new Deno.UnsafePointerView
.
Here's a quick recap of how you should be able to use Deno.UnsafePointerView
with a pointer returned from an FFI call:
If you're getting a type error when trying to pass resultPointer
to new Deno.UnsafePointerView
, it's possible that there might be a version mismatch or an issue with the Deno types. Make sure you are using the latest version of Deno, as the FFI API is still considered unstable and may have undergone changes.
If the issue persists, it could be a bug or a change in the FFI API that is not yet reflected in the documentation. In such cases, it's recommended to check the release notes for the version of Deno you are using or to look for any recent issues or discussions in the Deno repository on GitHub.
Please note that this advice is not based on the Deno documentation, and you should double-check the accuracy of the advice given. If you continue to experience difficulties, consider reaching out to the Deno community for further assistance.I'm sorry but
deno check
states otherwise: I understand that you're encountering a TypeScript error when trying to pass a
PointerValue<unknown>
to the Deno.UnsafePointerView
constructor. The error message suggests that TypeScript is expecting a PointerObject<unknown>
instead, and it's not allowing null
to be passed.
From the error message you've provided, it seems like there might be a type mismatch or an issue with the handling of potential null
values. Without the ability to consult the official Deno documentation for an answer to this specific issue, I can only provide general advice based on the error message itself.
One thing you could try is to check if the resultPtr
is not null
before attempting to create a new UnsafePointerView
. Here's an example of how you might guard against null
:
Please note that this advice is not based on the Deno documentation, and you should double-check the accuracy of the advice given. If the issue persists, it may be beneficial to look for recent changes or discussions in the Deno repository on GitHub, or to reach out to the Deno community for further assistance.Well..that solved it 🤡 .
For anyone that may get stuck with this in the future, here is the whole code for Deno + Hono API endpoint using the dinosaurs example earlier:
This is the Golang code used to generate the dynamic library for the Denosaurs example: you may compile this using:
go build -o ./<output_dir> -buildmode=c-shared .<input_go_file
.