Help with my first FFI bindings


#1

I’m attempting my first binding to a C library and had a few questions. Rather than breaking them into smaller posts, I thought I’d keep them in a single thread.

First, what is the point of bundling dependencies as both a sys crate, and as a higher-level API crate? Is it for versioning of the lower-level library separately from the Rust API? Are there reasons why you might just bundle them into a single crate?

Next, when I run the tests for my generated binding, I get one failure:

failures:

---- bindgen_test_layout_max_align_t stdout ----
	thread 'bindgen_test_layout_max_align_t' panicked at 'assertion failed: `(left == right)` (left: `24`, right: `32`): Size of: max_align_t', /home/nolan/Projects/speech-dispatcher-rs/speech-dispatcher-sys/target/debug/build/speech-dispatcher-sys-6b2956e71593ee84/out/speech-dispatcher.rs:1098

How do I fix this? What code do I even show that would help you help me to fix this? :slight_smile: I’d expect that bindgen generates tests that succeed, or that it at least tells me how to fix failing tests.

I have lower-level code that looks like this:

pub enum SPDConnectionMode { SPD_MODE_SINGLE = 0, SPD_MODE_THREADED = 1, }

I want to wrap it into a Rust API that looks like:

enum Mode {
    Single = 0,
    Threaded = 1,
}
...
    // Mode will be passed to a wrapped FFI call as an SPDConnectionMode.
    pub fn open(&self, client_name: String, connection_name: String, user_name: String, mode: Mode) -> Connection {

First, is there a way to get the struct values directly so I’m not hard-coding them in? Referring to them from code gives a type mismatch, since what I’m getting is the struct type and not its lower-level values. What I want to do is:

enum Mode {
    Single = SPDConnectionMode::SPD_MODE_SINGLE,
    Threaded = SPDConnectionMode::SPD_MODE_THREADED,
}

Similarly, calling the wrapped function doesn’t work. I suppose I could use pattern matching, but in this case the values align exactly, and I wish I could give the Rust API friendlier names while linking directly to the wrapped values somehow.

Thanks.


#2

Good questions!

Only one crate in the dependency graph is supposed to link to a native library. The idea is that the -sys crate is a direct representation of the library’s API and there is one and only one correct way to write this crate. The higher level API is probably more “opinionated” and different people would write it differently. These multiple different higher level crates would all use the same -sys crate to enforce the link-once paradigm.

This is probably a bug in bindgen. You can post the Rust definitions for bindgen_test_layout_max_align_t and max_align_t, and the original C definition.

There’s no great way to do this. If you encounter this often, you can use a macro. A big issue with FFI enums is that a value coming from C might actually not be one of the variants specified. This breaks Rust code since Rust enforces that enums never hold a value other than the specified variants. The standard way to handle this is to use integer types and constants at the lower level (bindgen has a configuration option for this).


#3
  • Nolan Darilek:

Next, when I run the tests for my generated binding, I get one failure:

failures:

---- bindgen_test_layout_max_align_t stdout ----
	thread 'bindgen_test_layout_max_align_t' panicked at 'assertion failed: `(left == right)` (left: `24`, right: `32`): Size of: max_align_t', /home/nolan/Projects/speech-dispatcher-rs/speech-dispatcher-sys/target/debug/build/speech-dispatcher-sys-6b2956e71593ee84/out/speech-dispatcher.rs:1098

How do I fix this? What code do I even show that would help you help me to fix this? :slight_smile: I’d expect that bindgen generates tests that succeed, or that it at least tells me how to fix failing tests.

max_align_t is a synthetic type, and only its alignment matters. It changed recently (in GCC 7).

Whatever generates bindings should ignore this type and not generate a Rust representation from it. It may be useful to extract the alignment as a constant, though.


#4

Thanks, all, for the help.

@fweimer, is there any way to tell bindgen not to generate bindings for this type? This library actually has a pretty small public interface, and it looks like there’s lots being pulled in that isn’t strictly necessary. So if I can omit this and make all tests pass, that’d be great.

@jethrogb, what is the config option to use integers/constants for enums, and can I apply it to just one? I scanned the README, but didn’t see anything promising. If there are more in depth docs then I’m happy to read those, but the README.md doesn’t appear to link to them.


#5

https://docs.rs/bindgen/0.23.1/bindgen/struct.Builder.html#method.constified_enum


#6

Perfect, thanks.


#7
  • Nolan Darilek:

@fweimer, is there any way to tell bindgen not to generate bindings
for this type? This library actually has a pretty small public
interface, and it looks like there’s lots being pulled in that isn’t
strictly necessary. So if I can omit this and make all tests pass,
that’d be great.

I’d suggest to file a bug against bindgen. max_align_t is rather special, so bindgen might need to know about this type and handle it in a different way.