ffi_opaque/
lib.rs

1#![no_std]
2
3#[doc(hidden)]
4pub use core as _core;
5
6/// Creates one or more types capable of representing opaque structs
7/// in FFI situations.
8///
9/// The resulting type:
10/// * cannot be constructed outside of the module it is defined in
11/// * ensures proper pointer alignment
12/// * is `!Send`, `!Sync`, `!Unpin`
13/// * is FFI safe
14///
15/// ## Example
16///
17/// Given the following C headers:
18///
19/// ```c
20/// typedef struct leveldb_options_t leveldb_options_t;
21///
22/// leveldb_options_t* leveldb_options_create();
23/// ```
24///
25/// We can represent the opaque struct `leveldb_options_t` on the Rust
26/// side like this:
27///
28/// ```rust
29/// use ffi_opaque::opaque;
30///
31/// opaque! {
32///     /// And we can document the type.
33///     pub struct leveldb_options_t;
34/// }
35///
36/// extern "C" {
37///     pub fn leveldb_options_create() -> *mut leveldb_options_t;
38/// }
39/// ```
40///
41/// ## Example 2
42///
43/// Multiple definitions are possible:
44///
45/// ```rust
46/// use ffi_opaque::opaque;
47///
48/// opaque! {
49///     /// Documentation for type_1;
50///     pub struct type_1;
51///     /// Documentation for type_2;
52///     pub struct type_2;
53/// }
54/// ```
55#[macro_export]
56macro_rules! opaque {
57    ($(
58        $(#[$meta:meta])*
59        $vis:vis struct $name:ident;
60    )+) => {$(
61        $(#[$meta])*
62        #[repr(C)]
63        $vis struct $name {
64            // Required for FFI-safe 0-sized type.
65            //
66            // In the future, this should refer to an extern type.
67            // See https://github.com/rust-lang/rust/issues/43467.
68            _data: [u8; 0],
69
70            // Required for !Send & !Sync & !Unpin.
71            //
72            // - `*mut u8` is !Send & !Sync. It must be in `PhantomData` to not
73            //   affect alignment.
74            //
75            // - `PhantomPinned` is !Unpin. It must be in `PhantomData` because
76            //   its memory representation is not considered FFI-safe.
77            _marker:
78                $crate::_core::marker::PhantomData<(*mut u8, $crate::_core::marker::PhantomPinned)>,
79        }
80    )+};
81}
82
83#[cfg(test)]
84pub mod test {
85    opaque! {
86        pub struct test_t;
87    }
88
89    static_assertions::assert_not_impl_any!(test_t: Send, Sync, Unpin);
90
91    static_assertions::assert_eq_size!(test_t, [u8; 0]);
92    static_assertions::assert_eq_align!(test_t, [u8; 0]);
93
94    #[deny(improper_ctypes, warnings)]
95    extern "C" {
96        pub fn f(_: *const test_t);
97    }
98}