Giter Club home page Giter Club logo

safer_ffi's Introduction

safer-ffi-banner

CI guide docs-rs crates-io repository

What is safer_ffi?

safer_ffi is a framework that helps you write foreign function interfaces (FFI) without polluting your Rust code with unsafe { ... } code blocks while making functions far easier to read and maintain.

๐Ÿ“š Read The User Guide ๐Ÿ“š

Prerequisites

Minimum Supported Rust Version: 1.66.1

Quickstart

Click to hide

Small self-contained demo

You may try working with the examples/point example embedded in the repo:

git clone https://github.com/getditto/safer_ffi && cd safer_ffi
(cd examples/point && make)

Otherwise, to start using ::safer_ffi, follow the following steps:

Crate layout

Step 1: Cargo.toml

Edit your Cargo.toml like so:

[package]
name = "crate_name"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = [
    "staticlib",  # Ensure it gets compiled as a (static) C library
  # "cdylib",     # If you want a shared/dynamic C library (advanced)
    "lib",        # For `generate-headers` and other downstream rust dependents
                  # such as integration `tests/`, doctests, and `examples/`
]

[[bin]]
name = "generate-headers"
required-features = ["headers"]  # Do not build unless generating headers.

[dependencies]
# Use `cargo add` or `cargo search` to find the latest values of x.y.z.
# For instance:
#   cargo add safer-ffi
safer-ffi.version = "x.y.z"
safer-ffi.features = [] # you may add some later on.

[features]
# If you want to generate the headers, use a feature-gate
# to opt into doing so:
headers = ["safer-ffi/headers"]
  • Where "x.y.z" ought to be replaced by the last released version, which you can find by running cargo search safer-ffi.

  • See the dedicated chapter on Cargo.toml for more info.

Step 2: src/lib.rs

Then, to export a Rust function to FFI, add the #[derive_ReprC] and #[ffi_export] attributes like so:

use ::safer_ffi::prelude::*;

/// A `struct` usable from both Rust and C
#[derive_ReprC]
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct Point {
    x: f64,
    y: f64,
}

/* Export a Rust function to the C world. */
/// Returns the middle point of `[a, b]`.
#[ffi_export]
fn mid_point(a: &Point, b: &Point) -> Point {
    Point {
        x: (a.x + b.x) / 2.,
        y: (a.y + b.y) / 2.,
    }
}

/// Pretty-prints a point using Rust's formatting logic.
#[ffi_export]
fn print_point(point: &Point) {
    println!("{:?}", point);
}

// The following function is only necessary for the header generation.
#[cfg(feature = "headers")] // c.f. the `Cargo.toml` section
pub fn generate_headers() -> ::std::io::Result<()> {
    ::safer_ffi::headers::builder()
        .to_file("rust_points.h")?
        .generate()
}

Step 3: src/bin/generate-headers.rs

fn main() -> ::std::io::Result<()> {
    ::crate_name::generate_headers()
}

Compilation & header generation

# Compile the C library (in `target/{debug,release}/libcrate_name.ext`)
cargo build # --release

# Generate the C header
cargo run --features headers --bin generate-headers
Generated C header (rust_points.h)
/*! \file */
/*******************************************
 *                                         *
 *  File auto-generated by `::safer_ffi`.  *
 *                                         *
 *  Do not manually edit this file.        *
 *                                         *
 *******************************************/

#ifndef __RUST_CRATE_NAME__
#define __RUST_CRATE_NAME__
#ifdef __cplusplus
extern "C" {
#endif


#include <stddef.h>
#include <stdint.h>

/** \brief
 *  A `struct` usable from both Rust and C
 */
typedef struct Point {
    /** <No documentation available> */
    double x;

    /** <No documentation available> */
    double y;
} Point_t;

/** \brief
 *  Returns the middle point of `[a, b]`.
 */
Point_t
mid_point (
    Point_t const * a,
    Point_t const * b);

/** \brief
 *  Pretty-prints a point using Rust's formatting logic.
 */
void
print_point (
    Point_t const * point);


#ifdef __cplusplus
} /* extern \"C\" */
#endif

#endif /* __RUST_CRATE_NAME__ */

Testing it from C

Here is a basic example to showcase FFI calling into our exported Rust functions:

main.c

#include <stdlib.h>

#include "rust_points.h"

int
main (int argc, char const * const argv[])
{
    Point_t a = { .x = 84, .y = 45 };
    Point_t b = { .x = 0, .y = 39 };
    Point_t m = mid_point(&a, &b);
    print_point(&m);
    return EXIT_SUCCESS;
}

Compilation command

cc -o main{,.c} -L target/debug -l crate_name -l{pthread,dl,m}

# Now feel free to run the compiled binary
./main
  • Note regarding the extra -lโ€ฆ flags.

    Those vary based on the version of the Rust standard library being used, and the system being used to compile it. In order to reliably know which ones to use, rustc itself ought to be queried for it.

    Simple command:

    rustc --crate-type=staticlib --print=native-static-libs -</dev/null

    this yields, to the stderr, output along the lines of:

    note: Link against the following native artifacts when linking against this static library. The order and any duplication can be significant on some platforms.
    
    note: native-static-libs: -lSystem -lresolv -lc -lm -liconv
    

    Using something like sed -nE 's/^note: native-static-libs: (.*)/\1/p' is thus a convenient way to extract these flags:

    rustc --crate-type=staticlib --print=native-static-libs -</dev/null \
        2>&1 | sed -nE 's/^note: native-static-libs: (.*)/\1/p'

    Ideally, you would not query for this information in a vacuum (e.g., /dev/null file being used as input Rust code just above), and rather, would apply it for your actual code being compiled:

    cargo rustc -q -- --print=native-static-libs \
        2>&1 | sed -nE 's/^note: native-static-libs: (.*)/\1/p'

    And if you really wanted to polish things further, you could use the JSON-formatted compiler output (this, for instance, avoids having to redirect stderr). But then you'd have to use a JSON parser, such as jq:

    RUST_STDLIB_DEPS=$(set -eo pipefail && \
        cargo rustc \
            --message-format=json \
            -- --print=native-static-libs \
        | jq -r '
            select (.reason == "compiler-message")
            | .message.message
        ' | sed -nE 's/^native-static-libs: (.*)/\1/p' \
    )

    and then use:

    cc -o main{,.c} -L target/debug -l crate_name ${RUST_STDLIB_DEPS}

which does output:

Point { x: 42.0, y: 42.0 }

๐Ÿš€๐Ÿš€

Development

Tests

safer-ffi includes three different tests suites that can be run.

# In the project root:
cargo test

# FFI tests

make -C ffi_tests

# JavaScript tests

make -C js_tests

# Running the JS tests also gives you instructions for running browser tests.
# Run this command in the `js_tests` directory, open a browser and navigate to
# http://localhost:13337/
wasm-pack build --target web && python3 -m http.server 13337

safer_ffi's People

Contributors

busec0 avatar danielhenrymantilla avatar dblouis avatar hamchapman avatar jsantell avatar ken0x0a avatar kriswuollett avatar mbalex99 avatar mw66 avatar p-avital avatar phatblat avatar phil-opp avatar pickfire avatar pixsperdavid avatar pvditto avatar rgwood avatar ronan-ditto avatar s4h avatar stefunctional avatar stevefan1999-personal avatar thebutlah avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

safer_ffi's Issues

Does char_p::Box implement AsRef<str>?

Trying to compile this:

struct Shift{}
pub trait GenericCollectionShift<S: Sized + Send + Sync + PartialEq + AsRef<str>> {
}
impl GenericCollectionShift<char_p::Box> for Shift {
}

works on macOS but fails on Ubuntu.
I'm getting

116 | impl GenericCollectionShift<char_p::Box> for Shift {
    |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsRef<str>` is not implemented for `char_p_boxed`
    |
note: required by a bound in `GenericCollectionShift`
   --> src/types.rs:13:71
    |
13  | pub trait GenericCollectionShift<S: Sized + Send + Sync + PartialEq + AsRef<str>> {
    |                                                                       ^^^^^^^^^^ required by this bound in `GenericCollectionShift`

Closures accepting structs with lifetimes

I ran into an annoying issue with BoxDynFnMut1 and friends when you try to pass around a struct with a non-'static lifetime due to the lack of higher-kinded types.

Basically, I want to let users set their own logging function. In normal Rust you'd write something like this:

pub fn set_logger(log_func: Box<dyn Fn(&log::Record<'_>)>) { ... }

The equivalent using safer_ffi would be (in theory) something like this:

#[ffi_export]
pub fn set_logger(log_func: for<'a> BoxDynFnMut1<(), LogRecord<'a>>) { ... }

#[derive_ReprC]
#[repr(C)]
pub struct LogRecord<'a> {
    level: LogLevel,
    target: str_ref<'a>,
}

Obviously for<'a> SomeStruct isn't valid syntax and you get an "Error: expected trait, found struct BoxDynFnMut1" error, however without the for<'a> you get "Error: cannot infer an appropriate lifetime due to conflicting requirements" when you try to use it in practice.

This is a concrete example of the "improve support for closures" mentioned in #47.

Generating headers for other platforms โ€“ Support for `#[cfg(targetโ€ฆ)]`-dependent signatures

Is there a way to generate header files for bindings which get cross-compiled? Currently, headers are generated by running a test on the host machine, but that assumes your test suite is executable.

I could bodge things together using a virtualisation layer like qemu and having the VM print the generated header to stdout, but that isn't ideal.

My use case is:

  • Create a bindings library which wraps your Rust crate
  • Generate pre-compiled libraries and headers for that library
  • Cross-compile from a Linux host (CI runner) to things like Android, iOS, or bare metal ARM
  • Conditional compilation is used to selectively enable functionality which is valid for the particular platform

Trying to build targeting Mac Catalyst which requires nightly but a dependency fails

If the use of mini_paste isn't too performance critical perhaps moving to paste would make sense since mini_paste hasn't been updated in a couple years.

   Compiling mini_paste v0.1.11
error[E0432]: unresolved imports `proc_macro::item`, `proc_macro::__expr_hack__`
  --> /Users/sam/.cargo/registry/src/github.com-1ecc6299db9ec823/mini_paste-0.1.11/src/lib.rs:11:5
   |
11 |     item as __item__,
   |     ----^^^^^^^^^^^^
   |     |
   |     no `item` in the root
   |     help: a similar name exists in the module: `iter`
12 |     __expr_hack__,
   |     ^^^^^^^^^^^^^ no `__expr_hack__` in the root

For more information about this error, try `rustc --explain E0432`.
error: could not compile `mini_paste` due to previous error

Problem: renaming types at the C header layer is not possible

Support renaming the types (at the C header layer) for:

  • Opaque types.

    • Syntax: #[ReprC::opaque("name_in_the_c_header")]
  • everything else

    • Idea of the syntax: #[ReprC::rename("name_in_the_c_header")]

      • would probably override the syntax for opaque types for the sake of consistency

      • #[derive_ReprC(rename = "...")] would also be possible, but the ::syn-less equivalent of the ReprC! { ... } macro would then be left out.

How to handle mutable arguments?

I want to add error reporting in my API along the lines of

#[derive_ReprC]
#[repr(C)]
pub struct Error {
    error_code: u32,
    error_str: char_p::Box,
}
Error error;
api_func(&error);
printf("%d: %s\n", error.error_code, error.error_str);
free_error(&error); // to free the memory allocated for `error_str`

(just an example, api_func() returns a result value too)

I tried various approaches on the Rust side, but I cannot make it work; at best it compiles, but in runtime I get a malloc: *** error for object <...>: pointer being freed was not allocated error. For example, for this code:

#[ffi_export]
fn api_func(error: &mut Error) {
    x.error_code = 0;
    x.error_str = "error".to_string().try_into().unwrap();
}

or for the example in #30. What is the correct way of doing it in safer_ffi? It seems that my problem is that I cannot figure out how to tell the compiler not to drop the newly created string.

(Ideally I'd want the reference to be nullable, so that one could call api_func(NULL) in C, and the error reporting would be skipped, but I'll probably be able to deduce it)

Support type aliases

It would be cool if we could tag things like this:

#[derive_ReprC]
#[ReprC::opaque]
pub type ContractDataHandle = arena::Index<ContractData>;

Any ideas how that would work? Would I still need to tag arena::Index with #[ReprC]?

Problem: Hard to bounce between Guide, Rust Gen Docs, and Github Repo

Problem

It is hard to bounce between Guide, Rust Gen Docs, and Github Repo.

Solution

  • Add GitHub Repo link in the guide sidebar
  • Add Rust Generated Docs in guide sidebar
  • Add GitHub Repo link in Rust Generated Docs Link
  • Add Guide link in Rust Generated Docs Link

More links the better, if you know of some link that we should add, just go ahead and add it!

Strange error: no method named `not` found

code (partial snippet, cant share all of it):

use crate::object::c_api::ObjectHandle as CObjectHandle;

#[ffi_export]
pub fn Baseline__object<'a>(baseline: &'a Baseline, handle: &CObjectHandle) -> &'a Object {
    baseline.object(handle.inner).unwrap()
}

and

pub type ObjectHandle = arena::Index<Object>;

#[cfg(feature = "c_api")]

pub mod c_api {
    use super::*;

    use derive_more::From;

    #[derive(From, Copy, Clone)]
    #[derive_ReprC]
    #[ReprC::opaque]
    pub struct ObjectHandle {
        pub inner: super::ObjectHandle,
    }
}

error[E0599]: no method named not found for type bool in the current scope
--> client/rust/src/baseline.rs:401:5
|
401 | #[ffi_export]
| ^^^^^^^^^^^^^ method not found in bool
|
::: /home/ryan/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/bit.rs:51:8
|
51 | fn not(self) -> Self::Output;
| --- the method is available for bool here
|
= help: items from traits can only be used if the trait is in scope
= note: this error originates in the macro ::safer_ffi::__ffi_export__ (in Nightly builds, run with -Z macro-backtrace for more info)
help: the following trait is implemented but not in scope; perhaps add a use for it:
|
392 | use std::ops::Not;
|

Problem: callbacks with lifetimes are not supported

ReprC is only implemented for extern "C" fn (A1, ..., Ak) -> Ret for any ReprC type A1, ..., Ak and Ret, and for k โ‰ค 9.

Sadly, a function such as extern "C" fn (&'_ i32) does not match the k = 1, A1 = &'_ i32, Ret = () case, since A1 there is not a type (which lifetime '_?).

Instead, such type is actually the type extern "C" for<'any> fn (&'any i32), that is, a higher-ranked type, which does not fit that generic impl.

Idea for a workaround

Granted, that fn() type cannot be "reached" by the generic impls. But given the knowledge of the actual concrete types involved (e.g., &'_ i32 -> ()), an impl for that specific case could be generated.

Sadly, that would be for the downstream user to do, so they wouldn't be able to impl that directly on the fn() type. Instead, a newtype wrapper would be necessary.

Hence the need for extending the functionality of #[derive_ReprC] so that:

#[derive_ReprC]
#[repr(transparent)]
pub struct MyCb /* = */ (
    pub extern "C" fn (&'_ i32),
);

is recognized, and thus allows people to have fields of type MyCb and use MyCb as the input or return type of an #[ffi_export]-ed function.

Quid of closures

Once we get to have extern "C" fn(...) -> _ involving higher-ranked lifetimes, another challenging part will be to manage to get ...dynFn... closure types also involving such higher-ranked lifetimes.

That last part is very likely to take more time to implement, though.

[Feature Request] Emit conditional compilation

A useful feature of cbindgen is the ability to specify a map of cfgs to defines so these can be emitted in the final header.

If possible this could be also a very useful feature for this project, if anything to make it a a more complete solution. Of course it's possible to do the header manually but that can get pretty cumbersome for bigger projects.

Using generics in struct does not create separate monomorphized types

I have a function that returns a repr_c::Box<MyOpaqueType<AnotherOpaqueType>>. It is generating on the C side as a * MyOpaqueType. instead of a MyOpaqueType_AnotherOpaqueType. This dilutes the type safety on the C side and makes it easier to accidentally cast a MyOpaqueType<A> to a MyOpaqueType<B>.

[Error] ReprC Implementation Not General Enough

I'm getting an error with this example, is there anything I can do about it?

use safer_ffi::prelude::*;

pub use ty::Void;
mod ty {
    use safer_ffi::derive_ReprC;

    #[derive_ReprC]
    #[ReprC::opaque]
    /// A type used to represent an untyped pointer
    pub struct Void {
        _private: (),
    }
}

#[derive_ReprC]
#[repr(C)]
#[derive(Clone)]
pub struct CHostFunctionPointers {
    /// Get the full CBOR serialized [`ScriptApi`] including components discovered and
    /// implemented by other language adapters or the dynamite host.
    pub get_full_api: extern "C" fn(dynamite: *const Void) -> repr_c::Vec<u8>,

    /// Call a function provided by the scripting API
    call_function: extern "C" fn(
        dynamite: *const Void,
        path: str::Ref,
        args: c_slice::Ref<*const Void>,
    ) -> *const Void,
}

#[derive_ReprC]
#[repr(C)]
pub struct RemoteHostFunctions {
    pub dynamite: *const Void,
    pub pointers: CHostFunctionPointers,
}
error[E0277]: the trait bound `<CHostFunctionPointers as ReprC>::CLayout: CType` is not satisfied
  --> src/lib.rs:32:1
   |
32 | #[derive_ReprC]
   | ^^^^^^^^^^^^^^^ the trait `CType` is not implemented for `<CHostFunctionPointers as ReprC>::CLayout`
   |
   = help: see issue #48214
   = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: implementation of `ReprC` is not general enough
  --> src/lib.rs:32:1
   |
32 | #[derive_ReprC]
   | ^^^^^^^^^^^^^^^ implementation of `ReprC` is not general enough
   |
   = note: `ReprC` would have to be implemented for the type `for<'r, 's> extern "C" fn(*const ty::Void, str_ref<'r>, slice_ref<'s, *const ty::Void>) -> *const ty::Void`
   = note: ...but `ReprC` is actually implemented for the type `extern "C" fn(*const ty::Void, str_ref<'0>, slice_ref<'_, *const ty::Void>) -> *const ty::Void`, for some specific lifetime `'0`
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: implementation of `ReprC` is not general enough
  --> src/lib.rs:32:1
   |
32 | #[derive_ReprC]
   | ^^^^^^^^^^^^^^^ implementation of `ReprC` is not general enough
   |
   = note: `ReprC` would have to be implemented for the type `for<'r, 's> extern "C" fn(*const ty::Void, str_ref<'r>, slice_ref<'s, *const ty::Void>) -> *const ty::Void`
   = note: ...but `ReprC` is actually implemented for the type `extern "C" fn(*const ty::Void, str_ref<'_>, slice_ref<'0, *const ty::Void>) -> *const ty::Void`, for some specific lifetime `'0`
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: implementation of `ReprC` is not general enough
  --> src/lib.rs:32:1
   |
32 | #[derive_ReprC]
   | ^^^^^^^^^^^^^^^ implementation of `ReprC` is not general enough
   |
   = note: `ReprC` would have to be implemented for the type `for<'r, 's> extern "C" fn(*const ty::Void, str_ref<'r>, slice_ref<'s, *const ty::Void>) -> *const ty::Void`
   = note: ...but `ReprC` is actually implemented for the type `extern "C" fn(*const ty::Void, str_ref<'0>, slice_ref<'_, *const ty::Void>) -> *const ty::Void`, for some specific lifetime `'0`
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: implementation of `ReprC` is not general enough
  --> src/lib.rs:32:1
   |
32 | #[derive_ReprC]
   | ^^^^^^^^^^^^^^^ implementation of `ReprC` is not general enough
   |
   = note: `ReprC` would have to be implemented for the type `for<'r, 's> extern "C" fn(*const ty::Void, str_ref<'r>, slice_ref<'s, *const ty::Void>) -> *const ty::Void`
   = note: ...but `ReprC` is actually implemented for the type `extern "C" fn(*const ty::Void, str_ref<'_>, slice_ref<'0, *const ty::Void>) -> *const ty::Void`, for some specific lifetime `'0`
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: implementation of `ReprC` is not general enough
  --> src/lib.rs:32:1
   |
32 | #[derive_ReprC]
   | ^^^^^^^^^^^^^^^ implementation of `ReprC` is not general enough
   |
   = note: `ReprC` would have to be implemented for the type `for<'r, 's> extern "C" fn(*const ty::Void, str_ref<'r>, slice_ref<'s, *const ty::Void>) -> *const ty::Void`
   = note: ...but `ReprC` is actually implemented for the type `extern "C" fn(*const ty::Void, str_ref<'0>, slice_ref<'_, *const ty::Void>) -> *const ty::Void`, for some specific lifetime `'0`
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: implementation of `ReprC` is not general enough
  --> src/lib.rs:32:1
   |
32 | #[derive_ReprC]
   | ^^^^^^^^^^^^^^^ implementation of `ReprC` is not general enough
   |
   = note: `ReprC` would have to be implemented for the type `for<'r, 's> extern "C" fn(*const ty::Void, str_ref<'r>, slice_ref<'s, *const ty::Void>) -> *const ty::Void`
   = note: ...but `ReprC` is actually implemented for the type `extern "C" fn(*const ty::Void, str_ref<'_>, slice_ref<'0, *const ty::Void>) -> *const ty::Void`, for some specific lifetime `'0`
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `<CHostFunctionPointers as ReprC>::CLayout: CType` is not satisfied
  --> src/lib.rs:32:1
   |
32 | #[derive_ReprC]
   | ^^^^^^^^^^^^^^^ the trait `CType` is not implemented for `<CHostFunctionPointers as ReprC>::CLayout`
   |
   = help: see issue #48214
   = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: implementation of `ReprC` is not general enough
  --> src/lib.rs:32:1
   |
32 | #[derive_ReprC]
   | ^^^^^^^^^^^^^^^ implementation of `ReprC` is not general enough
   |
   = note: `ReprC` would have to be implemented for the type `for<'r, 's> extern "C" fn(*const ty::Void, str_ref<'r>, slice_ref<'s, *const ty::Void>) -> *const ty::Void`
   = note: ...but `ReprC` is actually implemented for the type `extern "C" fn(*const ty::Void, str_ref<'0>, slice_ref<'_, *const ty::Void>) -> *const ty::Void`, for some specific lifetime `'0`
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: implementation of `ReprC` is not general enough
  --> src/lib.rs:32:1
   |
32 | #[derive_ReprC]
   | ^^^^^^^^^^^^^^^ implementation of `ReprC` is not general enough
   |
   = note: `ReprC` would have to be implemented for the type `for<'r, 's> extern "C" fn(*const ty::Void, str_ref<'r>, slice_ref<'s, *const ty::Void>) -> *const ty::Void`
   = note: ...but `ReprC` is actually implemented for the type `extern "C" fn(*const ty::Void, str_ref<'_>, slice_ref<'0, *const ty::Void>) -> *const ty::Void`, for some specific lifetime `'0`
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 10 previous errors

For more information about this error, try `rustc --explain E0277`.
error: could not compile `ffi_example`

Apparently it works with feature(trivial_bounds).

Edit: I don't think the trivial_bounds feature fixes everything, actually.

Redesign the trait / crate architecture so as to setup the state to more easily add automatic glue code generation for higher-level languages

Redesign the trait / crate architecture so as to setup the state to more easily add automatic glue code generation for higher-level languages:

  • move the header code from CType to ReprC;

  • parametrize the hard-coded C header code through a layer of "language syntax" indirection, since that layer of indirection ought to be the only thing "mobile" / changing from one SDK to another.

Rust Analyzer and c! macro

It appears rust-analyzer is producing false positives for the c! macro:
image

Running cargo check produces neither warnings nor errors.

Support unboxed opaque types that are `Copy`

It would be cool if there was support for safer_ffi opaque types that don't require boxing because they are Copy. Ostensibly, one could just look at the size and alignment of the type and create a C type with the same layout. This is what autocxx does and it seems to work well:

Generated rust code:

#[repr(C, align(4))]
pub struct HmdMatrix34_t {
    _pinned: core::marker::PhantomData<core::marker::PhantomPinned>,
    _non_send_sync: core::marker::PhantomData<[*const u8; 0]>,
    _data: [u8; 48],
}

Corresponding C/C++ POD type:

struct HmdMatrix34_t
{
	float m[3][4];
};

I'm assuming that this solution won't work for non-copy types (implying that they have a drop fn and cannot be memcopied), but in my API I have a bunch of opaque types that are Copy, and I'l really like to avoid boxing all of them, and having the caller have to allocate and drop them all the time

Rough edges or missing utility trait impls for some types

(Note, when tackled, each bullet point will have an associated specific issue or PR).

  • Out pointers to Option ought to be more easily constructible;

  • We are missing a repr_c::Result type;

  • We may want to loosen the alignment sanity-validity check requirement on the pointer part of a 0-long slice;

  • We are missing repr_c::Arc & co.;

  • The smart pointers, repr_c::Box<_>, and the to-be-added repr_c::Arc<_>, ought to forward as many traits as possible (e.g., PartialOrd)

  • We need more ergonomics for the non-char_p but still repr_c string types, which are currently buried under the string module, with no special mention anywhere;

  • More generally, we need more documentation, and more example for the most pervasive patterns!

cc @rodoyle @thombles @hamchapman, feel free to add other rough edges you may have found when working with safer-ffi, be it from Rust or from other languages.

ReprC Box of dyn object

The following example does not compile:

use safer_ffi::prelude::*;

#[ffi_export]
fn foobar(_: Option<repr_c::Box<dyn FooBar>>) {
    todo!()
}

It complains that dyn FooBar is not Sized but I fixed that with a quick hack, just adding ?Sized to a few places.

This error remains:

the trait `ReprC` is not implemented for `(dyn FooBar + 'static)`

Could this work eventually or is there some limitation I haven't think of?

Unions are not supported

#[derive_ReprC]
#[repr(C)]
#[derive(Copy, Clone)]
pub union Foobar {
    foo: i32,
    bar: f64,
}

This fails to compile with: error: `union`s are not supported yet.

Using #[derive_reprC] with derive attributes

Is there a way to use #[derive_reprC] with other derive attributes for enums?

When trying to compile:

#[derive_ReprC]
#[derive(PartialEq)]
#[repr(i32)]
pub enum Fmi2Status {
    Fmi2OK,
    Fmi2Warning,
    Fmi2Discard,
    Fmi2Error,
    Fmi2Fatal,
    Fmi2Pending,
}

I get:

63 | #[derive(PartialEq)]
   |   ^^^^^^ no rules expected this token in macro call

Compatibility with serde

I'm trying to define a struct that I'm using for serialization/deserialization with Serde like this:

use serde::{Deserialize, Serialize};

#[derive_ReprC]
#[repr(C)]
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
pub
struct Point {
    #[serde(rename = "x_value")] 
    x: f64,
    #[serde(rename = "y_value")] 
    y: f64,
}

However "safer_ffi" macros seem to generate a conflict since I get the following error at compile time:
cannot find attribute `serde` in this scope

I guess that since #[derive()] is not executed last, some definitions are missing in a previous stage?

Argument is not FFI-safe

The code below produces this warning:

warning: `extern` fn uses type `Option<slice_ref<Foo>>`, which is not FFI-safe
help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
use safer_ffi::prelude::*;

struct Foo<'a> {
    name: Option<char_p::Ref<'a>>,
}

#[ffi_export]
fn foo<'a>(
    p0: Option<char_p::Ref>, // this is fine...
    p1: Option<c_slice::Ref<'a, Foo<'a>>>, // ...but this is not?
) -> i32 {
    // do something...
}

Can't specify a custom C# namespace name

The package name is used as the C# namespace name but sometimes it would be useful to be able to customise the name used for the namespace so that it's different to the package name.

Generating header file from build.rs

I would like to generate the headers as part of my build.rs script, but it seems to not pick up the actual content currently, as it just generates the default header file, without my content.

Default include guard doesn't convert hyphens

Hi, first of all thanks for the awesome work!

Now, during the use of this library I noticed that my generated header include guard was taking the name I put on the crate manifest and using it but leaving the hyphens as is.

For example, a crate named my-awesome-crate, using the default builder, would generate a header with the include guard __RUST_MY-AWESOME-CRATE__

Customizable naming templates (e.g., namespace prefixing)

I was wondering if there is a way to automatically add a prefix to types/functions etc when generating the C definitions, sth like this:

#[ffi_export]
fn foo() {
  // ..things
}

#[derive_reprC]
pub struct MyStruct { /* ...  */ }
typedef struct { /* .. */ } PFX_MyStruct;
void pfx_foo() {
}

as currently I have to add the PFX prefix manually to my types and functions in rust, which makes it very unidiomatic, and easy to forget.

Idea: provide specialized support for specific languages

Such as:

The idea would be that Rust can currently identify "constructors" (would require a special attribute on the fn new() associated function), methods, and even the destructor, so it would be great if such semantically-rich semantics weren't lost by going through the flat C header layer, which currently requires people to manually recreate those semantics from scratch, out of SWIG or Cppsharp, etc. generated flat bindings.

Auto-generating stubs / shims for more higher-level classes would thus be a very neat thing to have.

  • like #[wasm_bindgen] does, for instance.

Customizable default typedef naming format

I feel like generated typedef should be either foobar_t or Foobar. Foobar_t seems wrong, but that's just my opinion. I understand that the ability to rename types is planned but in the meantime maybe the default format could be altered.

Missing libraries in README.md C compilation example

On my system (Ubuntu Linux) I had to add two more libraries (-lpthread -ldl) in the compilation/linking line for main.c:

$ cc main.c -o main -L target/debug -ltest_ffi -lpthread -ldl

Maybe worth mentioning this in the README.md file ...

unresolved macro error with rust-analyzer

I'm receiving the following error in my IDE when using rust-analyzer with VSCode for the default example:

unresolved macro ::safer_ffi::__ffi_export__!

It compiles and runs fine however.

mutable boxed types

What is the correct way to declare a mutable boxed type a function signature using #[ffi_export]?

Compiling with macro produces an error for the following piece of code:

#[ffi_export]
pub fn fmi2FreeInstance(mut slave: Option<repr_c::Box<Slave>>) {
    match slave.as_mut() {
        Some(s) => {
            s.rpc.fmi2FreeInstance();
            drop(slave)
        }
        None => {}
    }
}
244 | pub fn fmi2FreeInstance(mut slave: Option<repr_c::Box<Slave>>) {
    |                             ^^^^^ no rules expected this token in macro call

Without the macro the code compiles.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.