-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add C FFI Bindings #4
base: master
Are you sure you want to change the base?
Conversation
Any errors reported will cause the C API functions to return an error value (NULL or -1). The caller can then use: * const char* tts_get_error() to get a pointer to a string describing the error * void tts_clear_error() to deallocate any currently stored error message.
* tts_default() allocates a new TTS struct via it's default() constructor, returning a pointer to it or NULL on error. * tts_free(tts) destroys the TTS pointed to by tts. If tts is NULL, this function does nothing.
This required giving the Backends enum and Features struct a C representation.
The tts_speak() function has an additional parameter that, if not NULL, will be filled with a pointer to an UtteranceId. If this is specified, the caller must also call tts_free_utterance() to deallocate the UtteranceId when they're done with it.
This is better than using unsafe blocks inside the functions, as that tells the compiler that the unsafeness won't leak out of the block, which isn't true in this case as we're dealing with another unsafe language.
More portible, and the FFI is much more C-like.
…eaming snake case for enum variants.
Any errors reported will cause the C API functions to return an error value (NULL or -1). The caller can then use: * const char* tts_get_error() to get a pointer to a string describing the error * void tts_clear_error() to deallocate any currently stored error message.
* tts_default() allocates a new TTS struct via it's default() constructor, returning a pointer to it or NULL on error. * tts_free(tts) destroys the TTS pointed to by tts. If tts is NULL, this function does nothing.
This required giving the Backends enum and Features struct a C representation.
The tts_speak() function has an additional parameter that, if not NULL, will be filled with a pointer to an UtteranceId. If this is specified, the caller must also call tts_free_utterance() to deallocate the UtteranceId when they're done with it.
This is better than using unsafe blocks inside the functions, as that tells the compiler that the unsafeness won't leak out of the block, which isn't true in this case as we're dealing with another unsafe language.
More portible, and the FFI is much more C-like.
…eaming snake case for enum variants.
This currently always saves the bindings to target/release.
This insures the correct C standards are followed, but doesn't work on wasm32-unknown-unknown targets, because there *is* no libc. Given that the definition of `char` and `float` are very universal anyway, it makes sense to just use `i8` and `f32`.
I've now modified build.rs to automatically generate the C bindings and save them to I've also fixed the failing wasm test. This was caused by libc not defining |
This fixes an issue where the string pointer type in the generated bindings was const int8_t *. This works the same way, but could be confusing.
Can this be murged? It would be useful to use this in other languages because it not only provides cross-platform speech, but handles WinRT on Windows in a convenient manner, which can't be done in less mature languages like Dart or Go. |
Haven't looked into it, but was a port of at least the basic hello_world
example ever added? At the very least I need something I can build as
part of CI to ensure that the API remains compatible. Without that in
place, I have no way of knowing whether future changes break the API
since I myself am not using the C/C++ interface.
|
This is done because the ABI for returning struct values isn't well defined.
This PR adds extern "C" bindings to all functionality of the TTS struct, plus that of Features and Backends. The bindings are gated behind a new "ffi" Cargo feature.
Error Handling
Since we don't have the luxury of Result<T, E> in C, I created a thread-local static to store the last error in string form, and two functions:
tts_get_error()
to retrieve any errors.tts_clear_error()
to deallocate the last error (if any). This is probably unlikely to get used that often, since Rust's CString will deallocate itself when dropped.The caller can then determine if an error has occurred by examining the return value of most functions (if it is NULL, or false, something went wrong).
Generating the Bindings
I used cbindgen to create the C headers in testing, so included is a cbindgen.toml with some relatively sane settings.
One problem is detecting when we're compiling for iOS. I haven't found any way to do this that can be explained to cbindgen, so the headers would have to be modified manually. Consequently, the Apple section of the Backends enum currently looks like this:
Limitations
on_utterance_*
functions take a Boxed FnMut wide pointer.