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