Skip to content

Update documentation and API of Trivial and Opaque kind markers #333

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Oct 4, 2020
4 changes: 2 additions & 2 deletions macro/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream {

unsafe impl ::cxx::ExternType for #ident {
type Id = #type_id;
type Kind = ::cxx::Opaque;
type Kind = ::cxx::kind::Opaque;
}
}
}
Expand Down Expand Up @@ -692,7 +692,7 @@ fn expand_type_alias_kind_trivial_verify(type_alias: &TypeAlias) -> TokenStream
let end = quote_spanned!(end_span=> >);

quote! {
const _: fn() = #begin #ident, ::cxx::Trivial #end;
const _: fn() = #begin #ident, ::cxx::kind::Trivial #end;
}
}

Expand Down
107 changes: 67 additions & 40 deletions src/extern_type.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use self::kind::{Kind, Opaque, Trivial};

/// A type for which the layout is determined by its C++ definition.
///
/// This trait serves the following two related purposes.
Expand Down Expand Up @@ -69,11 +71,11 @@
/// # pub struct StringPiece([usize; 2]);
/// # }
///
/// use cxx::{type_id, ExternType, Opaque};
/// use cxx::{type_id, ExternType};
///
/// unsafe impl ExternType for folly_sys::StringPiece {
/// type Id = type_id!("folly::StringPiece");
/// type Kind = Opaque;
/// type Kind = cxx::kind::Opaque;
/// }
///
/// #[cxx::bridge(namespace = folly)]
Expand All @@ -93,29 +95,6 @@
/// #
/// # fn main() {}
/// ```
///
/// ## Opaque and Trivial types
///
/// Some C++ types are safe to hold and pass around in Rust, by value.
/// Those C++ types must have a trivial move constructor, and must
/// have no destructor.
///
/// If you believe your C++ type is indeed trivial, you can specify
/// ```
/// # struct TypeName;
/// # unsafe impl cxx::ExternType for TypeName {
/// type Id = cxx::type_id!("name::space::of::TypeName");
/// type Kind = cxx::Trivial;
/// # }
/// ```
/// which will enable you to pass it into C++ functions by value,
/// return it by value from such functions, and include it in
/// `struct`s that you have declared to `cxx::bridge`. Your promises
/// about the triviality of the C++ type will be checked using
/// `static_assert`s in the generated C++.
///
/// Opaque types can't be passed by value, but can still be held
/// in `UniquePtr`.
pub unsafe trait ExternType {
/// A type-level representation of the type's C++ namespace and type name.
///
Expand All @@ -125,32 +104,80 @@ pub unsafe trait ExternType {
/// # struct TypeName;
/// # unsafe impl cxx::ExternType for TypeName {
/// type Id = cxx::type_id!("name::space::of::TypeName");
/// type Kind = cxx::Opaque;
/// # type Kind = cxx::kind::Opaque;
/// # }
/// ```
type Id;

/// Either `cxx::Opaque` or `cxx::Trivial`. If in doubt, use
/// `cxx::Opaque`.
type Kind;
/// 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
/// trivial, 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;
}

pub(crate) mod 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 {}

/// An opaque type which can't be passed or held by value within Rust.
/// For example, a C++ type with a destructor, or a non-trivial move
/// constructor. Rust's strict move semantics mean that we can't own
/// these by value in Rust, but they can still be owned by a
/// `UniquePtr`...
pub struct Opaque;
pub trait Kind: private::Sealed {}
impl Kind for Opaque {}
impl Kind for Trivial {}
}

/// A type with trivial move constructors and no destructor, which
/// can therefore be owned and moved around in Rust code directly.
pub struct 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>() {}
pub fn verify_extern_kind<T: ExternType<Kind = Kind>, Kind: self::Kind>() {}
4 changes: 1 addition & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,9 +395,7 @@ mod unwind;
pub use crate::cxx_string::CxxString;
pub use crate::cxx_vector::CxxVector;
pub use crate::exception::Exception;
pub use crate::extern_type::kind::Opaque;
pub use crate::extern_type::kind::Trivial;
pub use crate::extern_type::ExternType;
pub use crate::extern_type::{kind, ExternType};
pub use crate::unique_ptr::UniquePtr;
pub use cxxbridge_macro::bridge;

Expand Down