Document ExternType trait
diff --git a/src/extern_type.rs b/src/extern_type.rs
index f7c376b..6701ef5 100644
--- a/src/extern_type.rs
+++ b/src/extern_type.rs
@@ -1,4 +1,108 @@
+/// 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 {
+/// extern "C" {
+/// type Demo;
+///
+/// fn create_demo() -> UniquePtr<Demo>;
+/// }
+/// }
+/// # }
+///
+/// // file2.rs
+/// #[cxx::bridge(namespace = example)]
+/// pub mod ffi {
+/// 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 an opaque 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");
+/// }
+///
+/// #[cxx::bridge(namespace = folly)]
+/// pub mod ffi {
+/// 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 Id;
}
diff --git a/src/lib.rs b/src/lib.rs
index 32d42a0..fc0e682 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -389,7 +389,10 @@
pub use crate::exception::Exception;
pub use crate::extern_type::ExternType;
pub use crate::unique_ptr::UniquePtr;
-pub use cxxbridge_macro::{bridge, type_id};
+pub use cxxbridge_macro::{bridge};
+
+/// For use in impls of the `ExternType` trait. See [`ExternType`].
+pub use cxxbridge_macro::type_id;
// Not public API.
#[doc(hidden)]
diff --git a/tests/ui/wrong_type_id.stderr b/tests/ui/wrong_type_id.stderr
index 969c826..5b4f6c6 100644
--- a/tests/ui/wrong_type_id.stderr
+++ b/tests/ui/wrong_type_id.stderr
@@ -1,13 +1,13 @@
error[E0271]: type mismatch resolving `<here::StringPiece as cxx::extern_type::ExternType>::Id == (cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)`
- --> $DIR/wrong_type_id.rs:11:9
- |
-11 | type ByteRange = crate::here::StringPiece;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected a tuple with 15 elements, found one with 17 elements
- |
- ::: $WORKSPACE/src/extern_type.rs:6:41
- |
-6 | pub fn verify_extern_type<T: ExternType<Id = Id>, Id>() {}
- | ------- required by this bound in `cxx::extern_type::verify_extern_type`
- |
- = note: expected tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)`
- found tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::S, cxx::t, cxx::r, cxx::i, cxx::n, cxx::g, cxx::P, cxx::i, cxx::e, cxx::c, cxx::e)`
+ --> $DIR/wrong_type_id.rs:11:9
+ |
+11 | type ByteRange = crate::here::StringPiece;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected a tuple with 15 elements, found one with 17 elements
+ |
+ ::: $WORKSPACE/src/extern_type.rs:110:41
+ |
+110 | pub fn verify_extern_type<T: ExternType<Id = Id>, Id>() {}
+ | ------- required by this bound in `cxx::extern_type::verify_extern_type`
+ |
+ = note: expected tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)`
+ found tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::S, cxx::t, cxx::r, cxx::i, cxx::n, cxx::g, cxx::P, cxx::i, cxx::e, cxx::c, cxx::e)`