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}