• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use self::kind::{Kind, Opaque, Trivial};
2 use crate::CxxString;
3 #[cfg(feature = "alloc")]
4 use alloc::string::String;
5 
6 /// A type for which the layout is determined by its C++ definition.
7 ///
8 /// This trait serves the following two related purposes.
9 ///
10 /// <br>
11 ///
12 /// ## Safely unifying occurrences of the same extern type
13 ///
14 /// `ExternType` makes it possible for CXX to safely share a consistent Rust
15 /// type across multiple #\[cxx::bridge\] invocations that refer to a common
16 /// extern C++ type.
17 ///
18 /// In the following snippet, two #\[cxx::bridge\] invocations in different
19 /// files (possibly different crates) both contain function signatures involving
20 /// the same C++ type `example::Demo`. If both were written just containing
21 /// `type Demo;`, then both macro expansions would produce their own separate
22 /// Rust type called `Demo` and thus the compiler wouldn't allow us to take the
23 /// `Demo` returned by `file1::ffi::create_demo` and pass it as the `Demo`
24 /// argument accepted by `file2::ffi::take_ref_demo`. Instead, one of the two
25 /// `Demo`s has been defined as an extern type alias of the other, making them
26 /// the same type in Rust. The CXX code generator will use an automatically
27 /// generated `ExternType` impl emitted in file1 to statically verify that in
28 /// file2 `crate::file1::ffi::Demo` really does refer to the C++ type
29 /// `example::Demo` as expected in file2.
30 ///
31 /// ```no_run
32 /// // file1.rs
33 /// # mod file1 {
34 /// #[cxx::bridge(namespace = "example")]
35 /// pub mod ffi {
36 ///     unsafe extern "C++" {
37 ///         type Demo;
38 ///
39 ///         fn create_demo() -> UniquePtr<Demo>;
40 ///     }
41 /// }
42 /// # }
43 ///
44 /// // file2.rs
45 /// #[cxx::bridge(namespace = "example")]
46 /// pub mod ffi {
47 ///     unsafe extern "C++" {
48 ///         type Demo = crate::file1::ffi::Demo;
49 ///
50 ///         fn take_ref_demo(demo: &Demo);
51 ///     }
52 /// }
53 /// #
54 /// # fn main() {}
55 /// ```
56 ///
57 /// <br><br>
58 ///
59 /// ## Integrating with bindgen-generated types
60 ///
61 /// Handwritten `ExternType` impls make it possible to plug in a data structure
62 /// emitted by bindgen as the definition of a C++ type emitted by CXX.
63 ///
64 /// By writing the unsafe `ExternType` impl, the programmer asserts that the C++
65 /// namespace and type name given in the type id refers to a C++ type that is
66 /// equivalent to Rust type that is the `Self` type of the impl.
67 ///
68 /// ```no_run
69 /// # const _: &str = stringify! {
70 /// mod folly_sys;  // the bindgen-generated bindings
71 /// # };
72 /// # mod folly_sys {
73 /// #     #[repr(transparent)]
74 /// #     pub struct StringPiece([usize; 2]);
75 /// # }
76 ///
77 /// use cxx::{type_id, ExternType};
78 ///
79 /// unsafe impl ExternType for folly_sys::StringPiece {
80 ///     type Id = type_id!("folly::StringPiece");
81 ///     type Kind = cxx::kind::Opaque;
82 /// }
83 ///
84 /// #[cxx::bridge(namespace = "folly")]
85 /// pub mod ffi {
86 ///     unsafe extern "C++" {
87 ///         include!("rust_cxx_bindings.h");
88 ///
89 ///         type StringPiece = crate::folly_sys::StringPiece;
90 ///
91 ///         fn print_string_piece(s: &StringPiece);
92 ///     }
93 /// }
94 ///
95 /// // Now if we construct a StringPiece or obtain one through one
96 /// // of the bindgen-generated signatures, we are able to pass it
97 /// // along to ffi::print_string_piece.
98 /// #
99 /// # fn main() {}
100 /// ```
101 pub unsafe trait ExternType {
102     /// A type-level representation of the type's C++ namespace and type name.
103     ///
104     /// This will always be defined using `type_id!` in the following form:
105     ///
106     /// ```
107     /// # struct TypeName;
108     /// # unsafe impl cxx::ExternType for TypeName {
109     /// type Id = cxx::type_id!("name::space::of::TypeName");
110     /// #     type Kind = cxx::kind::Opaque;
111     /// # }
112     /// ```
113     type Id;
114 
115     /// Either [`cxx::kind::Opaque`] or [`cxx::kind::Trivial`].
116     ///
117     /// [`cxx::kind::Opaque`]: kind::Opaque
118     /// [`cxx::kind::Trivial`]: kind::Trivial
119     ///
120     /// A C++ type is only okay to hold and pass around by value in Rust if its
121     /// [move constructor is trivial] and it has no destructor. In CXX, these
122     /// are called Trivial extern C++ types, while types with nontrivial move
123     /// behavior or a destructor must be considered Opaque and handled by Rust
124     /// only behind an indirection, such as a reference or UniquePtr.
125     ///
126     /// [move constructor is trivial]: https://en.cppreference.com/w/cpp/types/is_move_constructible
127     ///
128     /// If you believe your C++ type reflected by this ExternType impl is indeed
129     /// fine to hold by value and move in Rust, you can specify:
130     ///
131     /// ```
132     /// # struct TypeName;
133     /// # unsafe impl cxx::ExternType for TypeName {
134     /// #     type Id = cxx::type_id!("name::space::of::TypeName");
135     /// type Kind = cxx::kind::Trivial;
136     /// # }
137     /// ```
138     ///
139     /// which will enable you to pass it into C++ functions by value, return it
140     /// by value, and include it in `struct`s that you have declared to
141     /// `cxx::bridge`. Your claim about the triviality of the C++ type will be
142     /// checked by a `static_assert` in the generated C++ side of the binding.
143     type Kind: Kind;
144 }
145 
146 /// Marker types identifying Rust's knowledge about an extern C++ type.
147 ///
148 /// These markers are used in the [`Kind`][ExternType::Kind] associated type in
149 /// impls of the `ExternType` trait. Refer to the documentation of `Kind` for an
150 /// overview of their purpose.
151 pub mod kind {
152     use super::private;
153 
154     /// An opaque type which cannot be passed or held by value within Rust.
155     ///
156     /// Rust's move semantics are such that every move is equivalent to a
157     /// memcpy. This is incompatible in general with C++'s constructor-based
158     /// move semantics, so a C++ type which has a destructor or nontrivial move
159     /// constructor must never exist by value in Rust. In CXX, such types are
160     /// called opaque C++ types.
161     ///
162     /// When passed across an FFI boundary, an opaque C++ type must be behind an
163     /// indirection such as a reference or UniquePtr.
164     pub enum Opaque {}
165 
166     /// A type with trivial move constructor and no destructor, which can
167     /// therefore be owned and moved around in Rust code without requiring
168     /// indirection.
169     pub enum Trivial {}
170 
171     #[allow(missing_docs)]
172     pub trait Kind: private::Sealed {}
173     impl Kind for Opaque {}
174     impl Kind for Trivial {}
175 }
176 
177 mod private {
178     pub trait Sealed {}
179     impl Sealed for super::Opaque {}
180     impl Sealed for super::Trivial {}
181 }
182 
183 #[doc(hidden)]
verify_extern_type<T: ExternType<Id = Id>, Id>()184 pub fn verify_extern_type<T: ExternType<Id = Id>, Id>() {}
185 
186 #[doc(hidden)]
verify_extern_kind<T: ExternType<Kind = Kind>, Kind: self::Kind>()187 pub fn verify_extern_kind<T: ExternType<Kind = Kind>, Kind: self::Kind>() {}
188 
189 macro_rules! impl_extern_type {
190     ($([$kind:ident] $($(#[$($attr:tt)*])* $ty:path = $cxxpath:literal)*)*) => {
191         $($(
192             $(#[$($attr)*])*
193             unsafe impl ExternType for $ty {
194                 #[allow(unused_attributes)] // incorrect lint; this doc(hidden) attr *is* respected by rustdoc
195                 #[doc(hidden)]
196                 type Id = crate::type_id!($cxxpath);
197                 type Kind = $kind;
198             }
199         )*)*
200     };
201 }
202 
203 impl_extern_type! {
204     [Trivial]
205     bool = "bool"
206     u8 = "std::uint8_t"
207     u16 = "std::uint16_t"
208     u32 = "std::uint32_t"
209     u64 = "std::uint64_t"
210     usize = "size_t"
211     i8 = "std::int8_t"
212     i16 = "std::int16_t"
213     i32 = "std::int32_t"
214     i64 = "std::int64_t"
215     isize = "rust::isize"
216     f32 = "float"
217     f64 = "double"
218 
219     #[cfg(feature = "alloc")]
220     #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
221     String = "rust::String"
222 
223     [Opaque]
224     CxxString = "std::string"
225 }
226