tomleavy / safer-ffi-gen Goto Github PK
View Code? Open in Web Editor NEWAttribute macro to help automate Rust FFI bindings
License: MIT License
Attribute macro to help automate Rust FFI bindings
License: MIT License
A C callback should be fired when tokio_spawn
is done with the inner function that contains a custom result type if needed that can help users understand if there was an error or not.
Alternative:
on_success and on_failure
safer_ffi_gen
looks at the async
keyword on functions before maybe_async
has a chance to remove it if building with the is_sync
feature. This causes the function generated by safer_ffi_gen
to forward incorrectly the call to the original function as if it were async.
Async calls can be supported by using spawn_blocking
internally instead of having a callback
We have a use case where a function returns Option<u64>
. safer-ffi
currently supports Option<T>
when T
has a niche (to represent the None
case), which is not the case of u64
.
It seems to be limited to types with a niche (e.g. Box<T>
) in safer-ffi
0.0.10
. 0.1.0-rc1
addresses that, so let's try to update when it gets officially released. Support in safer-ffi-gen
should already be implemented, so this task is mainly/entirely about adding a test.
It should be possible to expose them as an opaque type (not exposing the variants as safer-ffi
does for C-like enums). Probably blocked on support in safer-ffi
unless we try to implement ReprC
ourselves.
Option works as a passthrough, but we need support for it defined
convert_case
turns FooI32
into foo_i_32
for snake-case, instead of foo_i32
. heck
gives the expected output.
To expose functions through FFI, safer-ffi-gen
requires adding the attribute macro safer_ffi_gen
to the impl
block and to annotate each function to expose with #[safer_ffi_gen]
, i.e.:
#[safer_ffi_gen]
impl Foo {
#[safer_ffi_gen]
pub fn foo() {}
pub fn bar() {}
}
In the above example, foo
is exposed through FFI but bar
isn't. Note that while #[safer_ffi_gen]
on each function looks like an attribute macro, it isn't. It is just an arbitrary token list that is recognized and removed by the #[safer_ffi_gen]
macro call on the impl
block.
This setup falls apart when making the FFI support conditionally compiled. First failed attempt:
#[cfg_attr(feature = "ffi", safer_ffi_gen)]
impl Foo {
#[safer_ffi_gen]
pub fn foo() {}
pub fn bar() {}
}
When the ffi
feature is off, the annotation on foo
isn't removed and thus the code does not compile. Second failed attempt:
#[cfg_attr(feature = "ffi", safer_ffi_gen)]
impl Foo {
#[cfg_attr(feature = "ffi", safer_ffi_gen)]
pub fn foo() {}
pub fn bar() {}
}
This fails to compile because the outer safer_ffi_gen
attribute only recognizes functions marked exactly with #[safer_ffi_gen]
. So in this case it leaves the inner cfg_attr
as-is, and that causes the inner safer_ffi_gen
macro to run on the fn foo
function, which leads to an error because this macro is meant to be applied to an impl
block instead of a function. Despite looking identical, #[safer_ffi_gen]
on a function is only an arbitrary marker to be consumed by the #[safer_ffi_gen]
macro on the impl
block.
The attribute on the impl
block is required because this is the only way to get the type name and generics.
The marker on functions could be removed and all functions in an impl
block with the attribute #[safer_ffi_gen]
would be exposed through FFI. The drawback of this approach is that it forces users to segregate functions in different impl
blocks based on whether they needs to be exposed through FFI. That can be a more invasive change for an existing codebase. An advantage of this solution is that it is easier to add to the existing safer-ffi-gen
implementation.
#[cfg_attr(feature = "ffi", safer_ffi_gen)]
impl Foo {
pub fn foo() {}
}
impl Foo {
pub fn bar() {}
}
The safer_ffi_gen
macro could be changed to handle both an impl
block and a function. When given an impl
block, it would inject the type name and generics into the safer_ffi_gen
attribute on functions inside the impl
block. This solution requires more work but provides more flexibility to users to arrange their functions.
The code would be written as follows:
#[cfg_attr(feature = "ffi", safer_ffi_gen)]
impl Foo {
#[cfg_attr(feature = "ffi", safer_ffi_gen)]
pub fn foo() {}
pub fn bar() {}
}
The expansion of the outer attribute would give something like:
impl Foo {
#[cfg_attr(feature = "ffi", safer_ffi_gen(Foo)]
pub fn foo() {}
pub fn bar() {}
}
use safer_ffi::prelude::c_slice
Make a trait that has an associated type so we can just print that directly instead of guessing based on the name of the struct
use safer_ffi::prelude::str
E.g. wrappers around int types, or structs whose fields all have a repr(C)
type.
Use basic types safer_ffi already supports such as i32 etc
Currently receivers &self self etc are not supported. These should be converted to parameters of the FFI method
Opaque types should be boxed when passed or returned by value.
C functions generated by safer-ffi-gen
may take or return pointers, but it is not clear when these pointers transfer ownership. E.g.:
While this information is lost in the C signatures, it is present in the Rust signatures (borrows vs owned values), so it seems that it should be possible to extract it and add it to the documentation of the generated C functions.
The pattern for a result should be a function that has any output as Out
and the function returns c_int.
There should also be a thread local err that you can get an error message from as a string with a function call
Currently [no_mangle] is being used for easier debugging
Basic testing to ensure that the output actually is valid
safer_ffi_gen should be able to be used in all cases. It is a no-op when called within a module that is also marked.
Currently using safer_ffi_gen_func as a marker just to get things started
This includes (at least) opaque types.
A slice parameter is translated to a struct with two fields: a pointer and a length. For a more idiomatic C experience, consider inlining these 2 fields as 2 parameters in the call.
FfiType
takes of generating a free
function for the type it is derived for. However, types like Vec<u8>
(and more generally Vec<T>
) or String
also need freeing.
Should the path start with ::safer_ffi
to make sure it picks up only external crates and not modules?
Originally posted by @stefunctional in #15 (comment)
In C, functions and types defined by a library often share a custom prefix identifying the library (thus emulating namespaces and avoiding name collisions). It would be helpful to support this.
An easy way is to specify this prefix everywhere one of our macros is used, but being able to define this prefix once and have it applied everywhere would be nice.
Ideally, support any executor.
The motivation is embedded projects which may have constraints on the executor to use.
Allow calling safer_ffi_gen::specialize
in a different module from the one containing the impl
block.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.