cxx/extern_type.rs
1use self::kind::{Kind, Opaque, Trivial};
2use crate::CxxString;
3#[cfg(feature = "alloc")]
4use 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/// ```
101pub 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.
151pub 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
177mod private {
178 pub trait Sealed {}
179 impl Sealed for super::Opaque {}
180 impl Sealed for super::Trivial {}
181}
182
183#[doc(hidden)]
184pub fn verify_extern_type<T: ExternType<Id = Id>, Id>() {}
185
186#[doc(hidden)]
187pub fn verify_extern_kind<T: ExternType<Kind = Kind>, Kind: self::Kind>() {}
188
189macro_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
203impl_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}