| #![doc = include_str!("../README.md")] |
| |
| // Copyright 2020 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your |
| // option. This file may not be copied, modified, or distributed |
| // except according to those terms. |
| |
| // The crazy macro_rules magic in this file is thanks to dtolnay@ |
| // and is a way of attaching rustdoc to each of the possible directives |
| // within the include_cpp outer macro. None of the directives actually |
| // do anything - all the magic is handled entirely by |
| // autocxx_macro::include_cpp_impl. |
| |
| mod rvalue_param; |
| pub mod subclass; |
| mod value_param; |
| |
| #[cfg_attr(doc, aquamarine::aquamarine)] |
| /// Include some C++ headers in your Rust project. |
| /// |
| /// This macro allows you to include one or more C++ headers within |
| /// your Rust code, and call their functions fairly naturally. |
| /// |
| /// # Examples |
| /// |
| /// C++ header (`input.h`): |
| /// ```cpp |
| /// #include <cstdint> |
| /// |
| /// uint32_t do_math(uint32_t a); |
| /// ``` |
| /// |
| /// Rust code: |
| /// ``` |
| /// # use autocxx_macro::include_cpp_impl as include_cpp; |
| /// include_cpp!( |
| /// # parse_only!() |
| /// #include "input.h" |
| /// generate!("do_math") |
| /// safety!(unsafe) |
| /// ); |
| /// |
| /// # mod ffi { pub fn do_math(a: u32) -> u32 { a+3 } } |
| /// # fn main() { |
| /// ffi::do_math(3); |
| /// # } |
| /// ``` |
| /// |
| /// The resulting bindings will use idiomatic Rust wrappers for types from the [cxx] |
| /// crate, for example [`cxx::UniquePtr`] or [`cxx::CxxString`]. Due to the care and thought |
| /// that's gone into the [cxx] crate, such bindings are pleasant and idiomatic to use |
| /// from Rust, and usually don't require the `unsafe` keyword. |
| /// |
| /// For full documentation, see [the manual](https://google.github.io/autocxx/). |
| /// |
| /// # The [`include_cpp`] macro |
| /// |
| /// Within the braces of the `include_cpp!{...}` macro, you should provide |
| /// a list of at least the following: |
| /// |
| /// * `#include "cpp_header.h"`: a header filename to parse and include |
| /// * `generate!("type_or_function_name")`: a type or function name whose declaration |
| /// should be made available to C++. (See the section on Allowlisting, below). |
| /// * Optionally, `safety!(unsafe)` - see discussion of [`safety`]. |
| /// |
| /// Other directives are possible as documented in this crate. |
| /// |
| /// Now, try to build your Rust project. `autocxx` may fail to generate bindings |
| /// for some of the items you specified with [generate] directives: remove |
| /// those directives for now, then see the next section for advice. |
| /// |
| /// # Allowlisting |
| /// |
| /// How do you inform autocxx which bindings to generate? There are three |
| /// strategies: |
| /// |
| /// * *Recommended*: provide various [`generate`] directives in the |
| /// [`include_cpp`] macro. This can specify functions or types. |
| /// * *Not recommended*: in your `build.rs`, call [`Builder::auto_allowlist`]. |
| /// This will attempt to spot _uses_ of FFI bindings anywhere in your Rust code |
| /// and build the allowlist that way. This is experimental and has known limitations. |
| /// * *Strongly not recommended*: use [`generate_all`]. This will attempt to |
| /// generate Rust bindings for _any_ C++ type or function discovered in the |
| /// header files. This is generally a disaster if you're including any |
| /// remotely complex header file: we'll try to generate bindings for all sorts |
| /// of STL types. This will be slow, and some may well cause problems. |
| /// Effectively this is just a debug option to discover such problems. Don't |
| /// use it! |
| /// |
| /// # Internals |
| /// |
| /// For documentation on how this all actually _works_, see |
| /// `IncludeCppEngine` within the `autocxx_engine` crate. |
| #[macro_export] |
| macro_rules! include_cpp { |
| ( |
| $(#$include:ident $lit:literal)* |
| $($mac:ident!($($arg:tt)*))* |
| ) => { |
| $($crate::$include!{__docs})* |
| $($crate::$mac!{__docs})* |
| $crate::include_cpp_impl! { |
| $(#include $lit)* |
| $($mac!($($arg)*))* |
| } |
| }; |
| } |
| |
| /// Include a C++ header. A directive to be included inside |
| /// [include_cpp] - see [include_cpp] for details |
| #[macro_export] |
| macro_rules! include { |
| ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; |
| } |
| |
| /// Generate Rust bindings for the given C++ type or function. |
| /// A directive to be included inside |
| /// [include_cpp] - see [include_cpp] for general information. |
| /// See also [generate_pod]. |
| #[macro_export] |
| macro_rules! generate { |
| ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; |
| } |
| |
| /// Generate as "plain old data" and add to allowlist. |
| /// Generate Rust bindings for the given C++ type such that |
| /// it can be passed and owned by value in Rust. This only works |
| /// for C++ types which have trivial move constructors and no |
| /// destructor - you'll encounter a compile error otherwise. |
| /// If your type doesn't match that description, use [generate] |
| /// instead, and own the type using [UniquePtr][cxx::UniquePtr]. |
| /// A directive to be included inside |
| /// [include_cpp] - see [include_cpp] for general information. |
| #[macro_export] |
| macro_rules! generate_pod { |
| ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; |
| } |
| |
| /// Generate Rust bindings for all C++ types and functions |
| /// in a given namespace. |
| /// A directive to be included inside |
| /// [include_cpp] - see [include_cpp] for general information. |
| /// See also [generate]. |
| #[macro_export] |
| macro_rules! generate_ns { |
| ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; |
| } |
| |
| /// Generate Rust bindings for all C++ types and functions |
| /// found. Highly experimental and not recommended. |
| /// A directive to be included inside |
| /// [include_cpp] - see [include_cpp] for general information. |
| /// See also [generate]. |
| #[macro_export] |
| macro_rules! generate_all { |
| ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; |
| } |
| |
| /// Generate as "plain old data". For use with [generate_all] |
| /// and similarly experimental. |
| #[macro_export] |
| macro_rules! pod { |
| ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; |
| } |
| |
| /// Skip the normal generation of a `make_string` function |
| /// and other utilities which we might generate normally. |
| /// A directive to be included inside |
| /// [include_cpp] - see [include_cpp] for general information. |
| #[macro_export] |
| macro_rules! exclude_utilities { |
| ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; |
| } |
| |
| /// Entirely block some type from appearing in the generated |
| /// code. This can be useful if there is a type which is not |
| /// understood by bindgen or autocxx, and incorrect code is |
| /// otherwise generated. |
| /// This is 'greedy' in the sense that any functions/methods |
| /// which take or return such a type will _also_ be blocked. |
| /// |
| /// A directive to be included inside |
| /// [include_cpp] - see [include_cpp] for general information. |
| #[macro_export] |
| macro_rules! block { |
| ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; |
| } |
| |
| /// Avoid generating implicit constructors for this type. |
| /// The rules for when to generate C++ implicit constructors |
| /// are complex, and if autocxx gets it wrong, you can block |
| /// such constructors using this. |
| /// |
| /// A directive to be included inside |
| /// [include_cpp] - see [include_cpp] for general information. |
| #[macro_export] |
| macro_rules! block_constructors { |
| ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; |
| } |
| |
| /// The name of the mod to be generated with the FFI code. |
| /// The default is `ffi`. |
| /// |
| /// A directive to be included inside |
| /// [include_cpp] - see [include_cpp] for general information. |
| #[macro_export] |
| macro_rules! name { |
| ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; |
| } |
| |
| /// A concrete type to make, for example |
| /// `concrete!("Container<Contents>")`. |
| /// All types msut already be on the allowlist by having used |
| /// `generate!` or similar. |
| /// |
| /// A directive to be included inside |
| /// [include_cpp] - see [include_cpp] for general information. |
| #[macro_export] |
| macro_rules! concrete { |
| ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; |
| } |
| |
| /// Specifies a global safety policy for functions generated |
| /// from these headers. By default (without such a `safety!` |
| /// directive) all such functions are marked as `unsafe` and |
| /// therefore can only be called within an `unsafe {}` block |
| /// or some `unsafe` function which you create. |
| /// |
| /// Alternatively, by specifying a `safety!` block you can |
| /// declare that most generated functions are in fact safe. |
| /// Specifically, you'd specify: |
| /// `safety!(unsafe)` |
| /// or |
| /// `safety!(unsafe_ffi)` |
| /// These two options are functionally identical. If you're |
| /// unsure, simply use `unsafe`. The reason for the |
| /// latter option is if you have code review policies which |
| /// might want to give a different level of scrutiny to |
| /// C++ interop as opposed to other types of unsafe Rust code. |
| /// Maybe in your organization, C++ interop is less scary than |
| /// a low-level Rust data structure using pointer manipulation. |
| /// Or maybe it's more scary. Either way, using `unsafe` for |
| /// the data structure and using `unsafe_ffi` for the C++ |
| /// interop allows you to apply different linting tools and |
| /// policies to the different options. |
| /// |
| /// Irrespective, C++ code is of course unsafe. It's worth |
| /// noting that use of C++ can cause unexpected unsafety at |
| /// a distance in faraway Rust code. As with any use of the |
| /// `unsafe` keyword in Rust, *you the human* are declaring |
| /// that you've analyzed all possible ways that the code |
| /// can be used and you are guaranteeing to the compiler that |
| /// no badness can occur. Good luck. |
| /// |
| /// Generated C++ APIs which use raw pointers remain `unsafe` |
| /// no matter what policy you choose. |
| #[macro_export] |
| macro_rules! safety { |
| ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; |
| } |
| |
| /// Whether to avoid generating [`cxx::UniquePtr`] and [`cxx::Vector`] |
| /// implementations. This is primarily useful for reducing test cases and |
| /// shouldn't be used in normal operation. |
| /// |
| /// A directive to be included inside |
| /// [include_cpp] - see [include_cpp] for general information. |
| #[macro_export] |
| macro_rules! exclude_impls { |
| ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; |
| } |
| |
| /// Indicates that a C++ type is not to be generated by autocxx in this case, |
| /// but instead should refer to some pre-existing Rust type. |
| /// Note that the size and alignment of this type *must* be correct. |
| /// If you wish for the type to be POD, you can use a `pod!` directive too. |
| /// |
| /// The syntax is: |
| /// `extern_cpp_type!("CppNameGoesHere", path::to::rust::type)` |
| /// |
| /// A directive to be included inside |
| /// [include_cpp] - see [include_cpp] for general information. |
| #[macro_export] |
| macro_rules! extern_cpp_type { |
| ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; |
| } |
| |
| /// Indicates that a C++ type is not to be generated by autocxx in this case, |
| /// but instead should refer to some pre-existing Rust type. Unlike |
| /// `extern_cpp_type!`, there's no need for the size and alignment of this |
| /// type to be correct. |
| /// |
| /// The syntax is: |
| /// `extern_cpp_opaque_type!("CppNameGoesHere", path::to::rust::type)` |
| /// |
| /// A directive to be included inside |
| /// [include_cpp] - see [include_cpp] for general information. |
| #[macro_export] |
| macro_rules! extern_cpp_opaque_type { |
| ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; |
| } |
| |
| /// Deprecated - use [`extern_rust_type`] instead. |
| #[macro_export] |
| #[deprecated] |
| macro_rules! rust_type { |
| ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; |
| } |
| |
| /// See [`extern_rust::extern_rust_type`]. |
| #[macro_export] |
| macro_rules! extern_rust_type { |
| ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; |
| } |
| |
| /// See [`subclass::subclass`]. |
| #[macro_export] |
| macro_rules! subclass { |
| ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; |
| } |
| |
| /// Indicates that a C++ type can definitely be instantiated. This has effect |
| /// only in a very specific case: |
| /// * the type is a typedef to something else |
| /// * the 'something else' can't be fully inspected by autocxx, possibly |
| /// becaue it relies on dependent qualified types or some other template |
| /// arrangement that bindgen cannot fully understand. |
| /// In such circumstances, autocxx normally has to err on the side of caution |
| /// and assume that some type within the 'something else' is itself a forward |
| /// declaration. That means, the opaque typedef won't be storable within |
| /// a [`cxx::UniquePtr`]. If you know that no forward declarations are involved, |
| /// you can declare the typedef type is instantiable and then you'll be able to |
| /// own it within Rust. |
| /// |
| /// The syntax is: |
| /// `instantiable!("CppNameGoesHere")` |
| /// |
| /// A directive to be included inside |
| /// [include_cpp] - see [include_cpp] for general information. |
| #[macro_export] |
| macro_rules! instantiable { |
| ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; |
| } |
| |
| #[doc(hidden)] |
| #[macro_export] |
| macro_rules! usage { |
| (__docs) => {}; |
| ($($tt:tt)*) => { |
| compile_error! {r#"usage: include_cpp! { |
| #include "path/to/header.h" |
| generate!(...) |
| generate_pod!(...) |
| } |
| "#} |
| }; |
| } |
| |
| use std::pin::Pin; |
| |
| #[doc(hidden)] |
| pub use autocxx_macro::include_cpp_impl; |
| |
| #[doc(hidden)] |
| pub use autocxx_macro::cpp_semantics; |
| |
| macro_rules! ctype_wrapper { |
| ($r:ident, $c:expr, $d:expr) => { |
| #[doc=$d] |
| #[derive(Debug, Eq, Copy, Clone, PartialEq, Hash)] |
| #[allow(non_camel_case_types)] |
| #[repr(transparent)] |
| pub struct $r(pub ::std::os::raw::$r); |
| |
| /// # Safety |
| /// |
| /// We assert that the namespace and type ID refer to a C++ |
| /// type which is equivalent to this Rust type. |
| unsafe impl cxx::ExternType for $r { |
| type Id = cxx::type_id!($c); |
| type Kind = cxx::kind::Trivial; |
| } |
| |
| impl From<::std::os::raw::$r> for $r { |
| fn from(val: ::std::os::raw::$r) -> Self { |
| Self(val) |
| } |
| } |
| |
| impl From<$r> for ::std::os::raw::$r { |
| fn from(val: $r) -> Self { |
| val.0 |
| } |
| } |
| }; |
| } |
| |
| ctype_wrapper!( |
| c_ulonglong, |
| "c_ulonglong", |
| "Newtype wrapper for an unsigned long long" |
| ); |
| ctype_wrapper!(c_longlong, "c_longlong", "Newtype wrapper for a long long"); |
| ctype_wrapper!(c_ulong, "c_ulong", "Newtype wrapper for an unsigned long"); |
| ctype_wrapper!(c_long, "c_long", "Newtype wrapper for a long"); |
| ctype_wrapper!( |
| c_ushort, |
| "c_ushort", |
| "Newtype wrapper for an unsigned short" |
| ); |
| ctype_wrapper!(c_short, "c_short", "Newtype wrapper for an short"); |
| ctype_wrapper!(c_uint, "c_uint", "Newtype wrapper for an unsigned int"); |
| ctype_wrapper!(c_int, "c_int", "Newtype wrapper for an int"); |
| ctype_wrapper!(c_uchar, "c_uchar", "Newtype wrapper for an unsigned char"); |
| |
| /// Newtype wrapper for a C void. Only useful as a `*c_void` |
| #[allow(non_camel_case_types)] |
| #[repr(transparent)] |
| pub struct c_void(pub ::std::os::raw::c_void); |
| |
| /// # Safety |
| /// |
| /// We assert that the namespace and type ID refer to a C++ |
| /// type which is equivalent to this Rust type. |
| unsafe impl cxx::ExternType for c_void { |
| type Id = cxx::type_id!(c_void); |
| type Kind = cxx::kind::Trivial; |
| } |
| |
| /// A C++ `char16_t` |
| #[allow(non_camel_case_types)] |
| #[repr(transparent)] |
| pub struct c_char16_t(pub u16); |
| |
| /// # Safety |
| /// |
| /// We assert that the namespace and type ID refer to a C++ |
| /// type which is equivalent to this Rust type. |
| unsafe impl cxx::ExternType for c_char16_t { |
| type Id = cxx::type_id!(c_char16_t); |
| type Kind = cxx::kind::Trivial; |
| } |
| |
| /// autocxx couldn't generate these bindings. |
| /// If you come across a method, type or function which refers to this type, |
| /// it indicates that autocxx couldn't generate that binding. A documentation |
| /// comment should be attached indicating the reason. |
| pub struct BindingGenerationFailure { |
| _unallocatable: [*const u8; 0], |
| _pinned: core::marker::PhantomData<core::marker::PhantomPinned>, |
| } |
| |
| /// Tools to export Rust code to C++. |
| // These are in a mod to avoid shadowing the definitions of the |
| // directives above, which, being macro_rules, are unavoidably |
| // in the crate root but must be function-style macros to keep |
| // the include_cpp impl happy. |
| pub mod extern_rust { |
| |
| /// Declare that this is a Rust type which is to be exported to C++. |
| /// You can use this in two ways: |
| /// * as an attribute macro on a Rust type, for instance: |
| /// ``` |
| /// # use autocxx_macro::extern_rust_type as extern_rust_type; |
| /// #[extern_rust_type] |
| /// struct Bar; |
| /// ``` |
| /// * as a directive within the [include_cpp] macro, in which case |
| /// provide the type path in brackets: |
| /// ``` |
| /// # use autocxx_macro::include_cpp_impl as include_cpp; |
| /// include_cpp!( |
| /// # parse_only!() |
| /// #include "input.h" |
| /// extern_rust_type!(Bar) |
| /// safety!(unsafe) |
| /// ); |
| /// struct Bar; |
| /// ``` |
| /// These may be used within references in the signatures of C++ functions, |
| /// for instance. This will contribute to an `extern "Rust"` section of the |
| /// generated `cxx` bindings, and this type will appear in the C++ header |
| /// generated for use in C++. |
| pub use autocxx_macro::extern_rust_type; |
| |
| /// Declare that a given function is a Rust function which is to be exported |
| /// to C++. This is used as an attribute macro on a Rust function, for instance: |
| /// ``` |
| /// # use autocxx_macro::extern_rust_function as extern_rust_function; |
| /// #[extern_rust_function] |
| /// pub fn call_me_from_cpp() { } |
| /// ``` |
| pub use autocxx_macro::extern_rust_function; |
| } |
| |
| /// Equivalent to [`std::convert::AsMut`], but returns a pinned mutable reference |
| /// such that cxx methods can be called on it. |
| pub trait PinMut<T>: AsRef<T> { |
| /// Return a pinned mutable reference to a type. |
| fn pin_mut(&mut self) -> std::pin::Pin<&mut T>; |
| } |
| |
| /// Provides utility functions to emplace any [`moveit::New`] into a |
| /// [`cxx::UniquePtr`]. Automatically imported by the autocxx prelude |
| /// and implemented by any (autocxx-related) [`moveit::New`]. |
| pub trait WithinUniquePtr { |
| type Inner: UniquePtrTarget + MakeCppStorage; |
| fn within_unique_ptr(self) -> cxx::UniquePtr<Self::Inner>; |
| } |
| |
| /// Provides utility functions to emplace any [`moveit::New`] into a |
| /// [`Box`]. Automatically imported by the autocxx prelude |
| /// and implemented by any (autocxx-related) [`moveit::New`]. |
| pub trait WithinBox { |
| type Inner; |
| fn within_box(self) -> Pin<Box<Self::Inner>>; |
| } |
| |
| use cxx::kind::Trivial; |
| use cxx::ExternType; |
| use moveit::MakeCppStorage; |
| use moveit::{Emplace, EmplaceUnpinned}; |
| |
| impl<N, T> WithinUniquePtr for N |
| where |
| N: New<Output = T>, |
| T: UniquePtrTarget + MakeCppStorage, |
| { |
| type Inner = T; |
| fn within_unique_ptr(self) -> cxx::UniquePtr<T> { |
| UniquePtr::emplace(self) |
| } |
| } |
| |
| impl<N, T> WithinBox for N |
| where |
| N: New<Output = T>, |
| { |
| type Inner = T; |
| fn within_box(self) -> Pin<Box<T>> { |
| Box::emplace(self) |
| } |
| } |
| |
| /// Emulates the [`WithinUniquePtr`] trait, but for trivial (plain old data) types. |
| /// This allows such types to behave identically if a type is changed from |
| /// `generate!` to `generate_pod!`. |
| /// |
| /// (Ideally, this would be the exact same trait as [`WithinUniquePtr`] but this runs |
| /// the risk of conflicting implementations. Negative trait bounds would solve |
| /// this!) |
| pub trait WithinUniquePtrTrivial: UniquePtrTarget + Sized + Unpin { |
| fn within_unique_ptr(self) -> cxx::UniquePtr<Self>; |
| } |
| |
| impl<T> WithinUniquePtrTrivial for T |
| where |
| T: UniquePtrTarget + ExternType<Kind = Trivial> + Sized + Unpin, |
| { |
| fn within_unique_ptr(self) -> cxx::UniquePtr<T> { |
| UniquePtr::new(self) |
| } |
| } |
| |
| /// Emulates the [`WithinBox`] trait, but for trivial (plain old data) types. |
| /// This allows such types to behave identically if a type is changed from |
| /// `generate!` to `generate_pod!`. |
| /// |
| /// (Ideally, this would be the exact same trait as [`WithinBox`] but this runs |
| /// the risk of conflicting implementations. Negative trait bounds would solve |
| /// this!) |
| pub trait WithinBoxTrivial: Sized + Unpin { |
| fn within_box(self) -> Pin<Box<Self>>; |
| } |
| |
| impl<T> WithinBoxTrivial for T |
| where |
| T: ExternType<Kind = Trivial> + Sized + Unpin, |
| { |
| fn within_box(self) -> Pin<Box<T>> { |
| Pin::new(Box::new(self)) |
| } |
| } |
| |
| use cxx::memory::UniquePtrTarget; |
| use cxx::UniquePtr; |
| use moveit::New; |
| pub use rvalue_param::RValueParam; |
| pub use rvalue_param::RValueParamHandler; |
| pub use value_param::as_copy; |
| pub use value_param::as_mov; |
| pub use value_param::as_new; |
| pub use value_param::ValueParam; |
| pub use value_param::ValueParamHandler; |
| |
| /// Imports which you're likely to want to use. |
| pub mod prelude { |
| pub use crate::as_copy; |
| pub use crate::as_mov; |
| pub use crate::as_new; |
| pub use crate::c_int; |
| pub use crate::c_long; |
| pub use crate::c_longlong; |
| pub use crate::c_short; |
| pub use crate::c_uchar; |
| pub use crate::c_uint; |
| pub use crate::c_ulong; |
| pub use crate::c_ulonglong; |
| pub use crate::c_ushort; |
| pub use crate::c_void; |
| pub use crate::cpp_semantics; |
| pub use crate::include_cpp; |
| pub use crate::PinMut; |
| pub use crate::RValueParam; |
| pub use crate::ValueParam; |
| pub use crate::WithinBox; |
| pub use crate::WithinBoxTrivial; |
| pub use crate::WithinUniquePtr; |
| pub use crate::WithinUniquePtrTrivial; |
| pub use cxx::UniquePtr; |
| pub use moveit::moveit; |
| pub use moveit::new::New; |
| pub use moveit::Emplace; |
| pub use moveit::EmplaceUnpinned; |
| } |
| |
| /// Re-export moveit for ease of consumers. |
| pub use moveit; |
| |
| /// Re-export cxx such that clients can use the same version as |
| /// us. This doesn't enable clients to avoid depending on the cxx |
| /// crate too, unfortunately, since generated cxx::bridge code |
| /// refers explicitly to ::cxx. See |
| /// <https://github.com/google/autocxx/issues/36> |
| pub use cxx; |