{{#title extern "C++" — Rust ♡ C++}} # extern "C++" ```rust,noplayground #[cxx::bridge] mod ffi { extern "C++" { include!("path/to/header.h"); include!("path/to/another.h"); ... } } ``` The `extern "C++"` section of a CXX bridge declares C++ types and signatures to be made available to Rust, and gives the paths of the header(s) which contain the corresponding C++ declarations. A bridge module may contain zero or more extern "C++" blocks. ## Opaque C++ types Type defined in C++ that are made available to Rust, but only behind an indirection. ```rust,noplayground # #[cxx::bridge] # mod ffi { extern "C++" { # include!("path/to/header.h"); # type MyType; type MyOtherType; } # } ``` For example in the ***[Tutorial](tutorial.md)*** we saw `BlobstoreClient` implemented as an opaque C++ type. The blobstore client was created in C++ and returned to Rust by way of a UniquePtr. **Mutability:** Unlike extern Rust types and shared types, an extern C++ type is not permitted to be passed by plain mutable reference `&mut MyType` across the FFI bridge. For mutation support, the bridge is required to use `Pin<&mut MyType>`. This is to safeguard against things like mem::swap-ing the contents of two mutable references, given that Rust doesn't have information about the size of the underlying object and couldn't invoke an appropriate C++ move constructor anyway. **Thread safety:** Be aware that CXX does not assume anything about the thread safety of your extern C++ types. In other words the `MyType` etc bindings which CXX produces for you in Rust *do not* come with `Send` and `Sync` impls. If you are sure that your C++ type satisfies the requirements of `Send` and/or `Sync` and need to leverage that fact from Rust, you must provide your own unsafe marker trait impls. ```rust,noplayground # #[cxx::bridge] # mod ffi { # extern "C++" { # include!("path/to/header.h"); # # type MyType; # } # } # /// The C++ implementation of MyType is thread safe. unsafe impl Send for ffi::MyType {} unsafe impl Sync for ffi::MyType {} ``` Take care in doing this because thread safety in C++ can be extremely tricky to assess if you are coming from a Rust background. For example the `BlobstoreClient` type in the tutorial is *not thread safe* despite doing only completely innocuous things in its implementation. Concurrent calls to the `tag` member function trigger a data race on the `blobs` map. ## Functions and member functions This largely follows the same principles as ***[extern "Rust"](extern-rust.md)*** functions and methods. In particular, any signature with a `self` parameter is interpreted as a C++ non-static member function and exposed to Rust as a method. The programmer **does not** need to promise that the signatures they have typed in are accurate; that would be unreasonable. CXX performs static assertions that the signatures exactly correspond with what is declared in C++. Rather, the programmer is only on the hook for things that C++'s static information is not precise enough to capture, i.e. things that would only be represented at most by comments in the C++ code unintelligible to a static assertion: namely whether the C++ function is safe or unsafe to be called from Rust. **Safety:** the extern "C++" block is responsible for deciding whether to expose each signature inside as safe-to-call or unsafe-to-call. If an extern block contains at least one safe-to-call signature, it must be written as an `unsafe extern` block, which serves as an item level unsafe block to indicate that an unchecked safety claim is being made about the contents of the block. ```rust,noplayground #[cxx::bridge] mod ffi { unsafe extern "C++" { # include!("path/to/header.h"); # fn f(); // safe to call } extern "C++" { unsafe fn g(); // unsafe to call } } ``` ## Lifetimes C++ types holding borrowed data may be described naturally in Rust by an extern type with a generic lifetime parameter. For example in the case of the following pair of types: ```cpp // header.h class Resource; class TypeContainingBorrow { TypeContainingBorrow(const Resource &res) : res(res) {} const Resource &res; }; std::shared_ptr create(const Resource &res); ``` we'd want to expose this to Rust as: ```rust,noplayground #[cxx::bridge] mod ffi { unsafe extern "C++" { # include!("path/to/header.h"); # type Resource; type TypeContainingBorrow<'a>; fn create<'a>(res: &'a Resource) -> SharedPtr>; // or with lifetime elision: fn create(res: &Resource) -> SharedPtr; } } ``` ## Reusing existing binding types Extern C++ types support a syntax for declaring that a Rust binding of the correct C++ type already exists outside of the current bridge module. This avoids generating a fresh new binding which Rust's type system would consider non-interchangeable with the first. ```rust,noplayground #[cxx::bridge(namespace = "path::to")] mod ffi { extern "C++" { type MyType = crate::existing::MyType; } extern "Rust" { fn f(x: &MyType) -> usize; } } ``` In this case rather than producing a unique new Rust type `ffi::MyType` for the Rust binding of C++'s `::path::to::MyType`, CXX will reuse the already existing binding at `crate::existing::MyType` in expressing the signature of `f` and any other uses of `MyType` within the bridge module. CXX safely validates that `crate::existing::MyType` is in fact a binding for the right C++ type `::path::to::MyType` by generating a static assertion based on `crate::existing::MyType`'s implementation of [`ExternType`], which is a trait automatically implemented by CXX for bindings that it generates but can also be manually implemented as described below. [`ExternType`]: https://docs.rs/cxx/*/cxx/trait.ExternType.html `ExternType` serves the following two related use cases. #### Safely unifying occurrences of an extern type across bridges 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. ```rust,noplayground // file1.rs #[cxx::bridge(namespace = "example")] pub mod ffi { unsafe extern "C++" { type Demo; fn create_demo() -> UniquePtr; } } ``` ```rust,noplayground // file2.rs #[cxx::bridge(namespace = "example")] pub mod ffi { unsafe extern "C++" { type Demo = crate::file1::ffi::Demo; fn take_ref_demo(demo: &Demo); } } ``` #### Integrating with bindgen-generated or handwritten unsafe bindings 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. ```rust,noplayground mod folly_sys; // the bindgen-generated bindings 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. ``` The `ExternType::Id` associated type encodes a type-level representation of the type's C++ namespace and type name. It will always be defined using the `type_id!` macro exposed in the cxx crate. The `ExternType::Kind` associated type will always be either [`cxx::kind::Opaque`] or [`cxx::kind::Trivial`] identifying whether a C++ type is soundly relocatable by Rust's move semantics. 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. [`cxx::kind::Opaque`]: https://docs.rs/cxx/*/cxx/kind/enum.Opaque.html [`cxx::kind::Trivial`]: https://docs.rs/cxx/*/cxx/kind/enum.Trivial.html [move constructor is trivial]: https://en.cppreference.com/w/cpp/types/is_move_constructible If you believe your C++ type reflected by the ExternType impl is indeed fine to hold by value and move in Rust, you can specify: ```rust,noplayground # 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. ## Explicit shim trait impls This is a somewhat niche feature, but important when you need it. CXX's support for C++'s std::unique\_ptr and std::vector is built on a set of internal trait impls connecting the Rust API of UniquePtr and CxxVector to underlying template instantiations performed by the C++ compiler. When reusing a binding type across multiple bridge modules as described in the previous section, you may find that your code needs some trait impls which CXX hasn't decided to generate. ```rust,noplayground #[cxx::bridge] mod ffi1 { extern "C++" { include!("path/to/header.h"); type A; type B; // Okay: CXX sees UniquePtr using a type B defined within the same // bridge, and automatically emits the right template instantiations // corresponding to std::unique_ptr. fn get_b() -> UniquePtr; } } #[cxx::bridge] mod ffi2 { extern "C++" { type A = crate::ffi1::A; // Rust trait error: CXX processing this module has no visibility into // whether template instantiations corresponding to std::unique_ptr // have already been emitted by the upstream library, so it does not // emit them here. If the upstream library does not have any signatures // involving UniquePtr, an explicit instantiation of the template // needs to be requested in one module or the other. fn get_a() -> UniquePtr; } } ``` You can request a specific template instantiation at a particular location in the Rust crate hierarchy by writing `impl UniquePtr {}` inside of the bridge module which defines `A` but does not otherwise contain any use of `UniquePtr`. ```rust,noplayground #[cxx::bridge] mod ffi1 { extern "C++" { include!("path/to/header.h"); type A; type B; fn get_b() -> UniquePtr; } impl UniquePtr {} // explicit instantiation } ```