cxx_enumext/lib.rs
1/*
2 * Copyright (c) Rachel Powers.
3 *
4 * This source code is licensed under both the MIT license found in the
5 * LICENSE-MIT file in the root directory of this source tree and the Apache
6 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
7 * of this source tree.
8 */
9
10//! `cxx-enumext` is a Rust crate that extends the [`cxx`](http://cxx.rs/) library to provide
11//! a mapping between Rust variant enums and c++ standards like `std::variant`, `std::optional`
12//! and `std::expected`. Unfortunately the memory modal and null pointer optimisations in play
13//! make direct mappings between the two extremely difficult. As such, `cxx-enumext` provides
14//! analogs which should be identical in usage.
15//!
16//! `rust::enm::variant` is a analog for `std::variant` which can fully map a `#[repr(c)]` Rust
17//! Enum. The `#[cxx_enumext::extern_type]` macro will transform yur enums apropreatly as well as
18//! provide some sanity check static assertions about their contents for the current limitations of cxx
19//! ffi.
20//!
21//! `rust::enum::optional` is a derivation from `rust::enm::variant` which provides the interface of
22//! `std::optional`
23//!
24//! `rust::enum::expected` is a derivation from `rust::enm::variant` which provides the interface of
25//! `std::expected`
26//!
27//! ## Quick tutorial
28//!
29//! To use `cxx-enumext`, first start by adding `cxx` to your project. Then add the following to your
30//! `Cargo.toml`:
31//!
32//! ```toml
33//! [dependencies]
34//! cxx-enumext = "0.1"
35//! ```
36//!
37//! If your going to put other extern(d) types or shared types inside your enums declare them
38//! in a seperate module so that you don't deal with the c++ compiler complaing
39//! about incompleate types
40//!
41//! ```rust
42//! //! my_crate/src/data.rs
43//!
44//! #[cxx::bridge]
45//! pub mod ffi {
46//!
47//! #[derive(Debug)]
48//! struct SharedData {
49//! size: i64,
50//! tags: Vec<String>,
51//! }
52//!
53//! extern "Rust" {
54//! type RustValue;
55//! fn read(&self) -> &String;
56//!
57//! fn new_rust_value() -> Box<RustValue>;
58//! }
59//! }
60//!
61//! fn new_rust_value() -> Box<RustValue> {
62//! Box::new(RustValue {
63//! value: "A Hidden Rust String".into(),
64//! })
65//! }
66//!
67//! #[derive(Default, Debug, Clone)]
68//! pub struct RustValue {
69//! value: String,
70//! }
71//!
72//! impl RustValue {
73//! pub fn read(&self) -> &String {
74//! &self.value
75//! }
76//! pub fn new(val: &str) -> Self {
77//! RustValue {
78//! value: val.to_string(),
79//! }
80//! }
81//! }
82//! ```
83//!
84//! Now, simply declare your enum, optional, or expected
85//!
86//! ```rust
87//! //! my_crate/src/enum.rs
88//!
89//! use data::{RustValue, ffi::SharedData};
90//!
91//! // enums -> variants are declared as is
92//! // all variant types are supported
93//! // (Unnamed struct, named structs, unit, single type wrappers)
94//! #[derive(Debug)]
95//! #[cxx_enumext::extern_type]
96//! pub enum RustEnum<'a> {
97//! Empty,
98//! Num(i64),
99//! String(String),
100//! Bool(bool),
101//! Shared(SharedData),
102//! SharedRef(&'a SharedData),
103//! Opaque(Box<RustValue>),
104//! OpaqueRef(&'a RustValue),
105//! Tuple(i32, i32),
106//! Struct { val: i32, str: String },
107//! Unit1,
108//! Unit2,
109//! }
110//!
111//! // Declare type aliases for an Optional, "alias" can be for `Optional<T>` or
112//! // `cxx_enumext::Optional<T>`, neither type truely exists but the name will be
113//! // used to determine the enum variants to generate
114//!
115//! // `cxx_name` and `namespace` are supported and function identically to `cxx`
116//! #[cxx_enumext::extern_type(cxx_name = "OptionalInt32")]
117//! #[derive(Debug)]
118//! pub type OptionalI32 = Optional<i32>;
119//!
120//!
121//! // Declare type aliases for an Expected, "alias" can be for `Expected<T,E>` or
122//! // `cxx_enumext::Expected<T,E>`, neither type truely exists but the name will be
123//! // used to determine the enum variants to generate
124//! #[cxx_enumext::extern_type]
125//! #[derive(Debug)]
126//! pub type I32StringResult = cxx_enumext::Expected<i32, String>;
127//!
128//!
129//! #[cxx::bridge]
130//! pub mod ffi {
131//!
132//! unsafe extern "C++" {
133//! include!("my_crate/src/enum.h");
134//!
135//! type RustEnum<'a> = super::RustEnum<'a>;
136//! type I32StringResult = super::I32StringResult;
137//! type OptionalInt32 = super::OptionalI32;
138//! }
139//! }
140//! ```
141//!
142//! This will generate the `cxx::ExternType` impl as well as some non-exhaustive
143//! static assertions to check that contained types are at least externable.
144//!
145//! for `Optional` and `Expected` types some `std::convert::From<T>` impls will be cenerated
146//! to convert from and to `Option` and `Result` types
147//!
148//! Now, in your C++ file, make sure to `#include` the right headers:
149//!
150//! ```cpp
151//! #include "rust/cxx.h"
152//! #include "rust/cxx_enumext.h"
153//! #include "rust/cxx_enumext_macros.h"
154//! ```
155//!
156//! And add a call to the `CXX_DEFINE_VARIANT`, `CXXASYNC_DEFINE_OPTIONAL`,
157//! and or `CXXASYNC_DEFINE_EXPECTED` macro as apropreate to define the C++ side of the types:
158//!
159//! ```cpp
160//! // my_crate/src/enum.h
161//!
162//! #include "rust/cxx.h"
163//! #include "rust/cxx_enumext.h"
164//! #include "rust/cxx_enumext_macros.h"
165//!
166//! #include "my_crate/src/data.rs.h"
167//!
168//! // The first argument is the name of the C++ type, and the second argument is a parentheses
169//! // enclosed list of variant directives to devine the inner variants.
170//! // They must be declared in the same order as the rust enum and specify the appropriate C++ type.
171//! // An optional third (actualy variadic) argument is placed verbatim in the resulting struct body.
172//! //
173//! // This macro must be invoked in the correct namespace to define the type.
174//! CXX_DEFINE_VARIANT(
175//! RustEnum,
176//! (UNIT(Empty) /*(Alias name)*/, TYPE(Num, int64_t) /*(Alias name, type)*/,
177//! TYPE(String, rust::string) /*by value rust type*/,
178//! TYPE(Bool, bool) /*primative type*/,
179//! TYPE(Shared, SharedData) /*shared type*/,
180//! TYPE(
181//! SharedRef,
182//! std::reference_wrapper<SharedData>) /* if your using a refrence wrap is
183//! in a `std::reference_wrapper`*/
184//! ,
185//! TYPE(Opaque, rust::box<RustValue>) /* boxed opaque rust type*/,
186//! TYPE(OpaqueRef,
187//! std::reference_wrapper<RustValue>) /*refren to opaque rust type*/,
188//! TUPLE(Tuple, int32_t, int32_t) /*(Alias name, [list, of, types]) => struct
189//! aliast_t{ T1 _0; T2 _1; ...};*/
190//! ,
191//! STRUCT(Struct, int32_t val;
192//! rust::string str;) /*(Alias name, body of struct for members) struct
193//! alist_t{body}; */
194//! ,
195//! UNIT(Unit1) /*More the one unit type supported, each is just a `struct
196//! alias_t {};`*/
197//! ,
198//! UNIT(Unit2) /*NO TRAILING COMMA*/
199//! ),
200//! /* optional section injected into the type body (for declareing member
201//! function etc.) */
202//! /* DO NOT DECLARE EXTRA MEMBER VARIABLES, THIS WILL MAKE THE TYPE HAVE */
203//! /* AN INCORRECT MEMORY LAYOUT */
204//! )
205//!
206//!
207//! // The first argument is the name of the C++ type, and the second contained type
208//! // An optional third (actualy variadic) argument is placed verbatim in the resulting struct body.
209//! CXX_DEFINE_OPTIONAL(OptionalInt32, int32_t)
210//!
211//! // The first argument is the name of the C++ type, and the second and third
212//! // are the expected and unexpected types respectively.
213//! // An optional forth (actualy variadic) argument is placed verbatim in the resulting struct body.
214//! CXX_DEFINE_EXPECTED(I32StringResult, int32_t, rust::string)
215//!
216//! ```
217//!
218//! You're all set! Now you can use the enum types on either side of your ffi.
219//!
220//! ## Installation notes
221//!
222//! You will need a C++ compiler that implements c++17
223//!
224//! ## Refrence
225//!
226//! ### `rust::enm::variant`
227//!
228//! Simplicited declaration
229//!
230//! ```c++
231//!
232//! namespace rust {
233//! namespace enm {
234//!
235//! /// @brief The getf method which returns a pointer to T if the variant
236//! // contains T, otherwise throws `bad_rust_variant_access`
237//! template <std::size_t I, typename... Ts>
238//! constexpr decltype(auto) get(variant_base<Ts...> &);
239//!
240//! template <std::size_t I, typename... Ts>
241//! constexpr decltype(auto) get(const variant_base<Ts...> &);
242//!
243//! /// @brief The get_if method which returns a pointer to T if the variant
244//! /// contains T other wise `nullptr`.
245//! template <std::size_t I, typename... Ts>
246//! constexpr std::add_pointer_t<variant_alternative_t<I, Ts...>>
247//! get_if(variant_base<Ts...> *variant);
248//!
249//! template <std::size_t I, typename... Ts>
250//! constexpr const std::add_pointer_t<variant_alternative_t<I, Ts...>>
251//! get_if(const variant_base<Ts...> *variant);
252//!
253//! /// @brief The visit method which will pick the right type depending on the
254//! /// `index` value.
255//! template <typename Visitor, typename... Ts>
256//! constexpr decltype(auto) visit(Visitor &&visitor, variant_base<Ts...> &);
257//!
258//! template <typename Visitor, typename... Ts>
259//! constexpr decltype(auto) visit(Visitor &&visitor, const variant_base<Ts...> &);
260//!
261//!
262//! /// @brief The holds_alternative method which will return true if the
263//! /// variant contains the type at index I
264//! template <std::size_t I, typename... Ts>
265//! constexpr bool holds_alternative(const variant_base<Ts...> &variant);
266//!
267//! /// @brief The holds_alternative method which will return true if the
268//! /// variant contains T
269//! template <typename T, typename... Ts,
270//! typename = std::enable_if_t<
271//! exactly_once<std::is_same_v<Ts, std::decay_t<T>>...>::value>>
272//! constexpr bool holds_alternative(const variant_base<Ts...> &variant);
273//!
274//! template <typename... Ts> struct variant {
275//! /// @brief Converting constructor. Corresponds to (4) constructor of
276//! /// std::variant.
277//! template <typename T, typename D = std::decay_t<T>,
278//! typename = std::enable_if_t<is_unique_v<T> &&
279//! std::is_constructible_v<D, T>>>
280//! variant(T &&other) noexcept(std::is_nothrow_constructible_v<D, T>);
281//!
282//! /// @brief Participates in the resolution only if we can construct T from
283//! /// Args and if T is unique in Ts. Corresponds to (5) constructor of
284//! /// std::variant.
285//! template <typename T, typename... Args,
286//! typename = std::enable_if_t<is_unique_v<T>>,
287//! typename = std::enable_if_t<std::is_constructible_v<T, Args...>>>
288//! explicit variant(std::in_place_type_t<T> type, Args &&...args) noexcept(
289//! std::is_nothrow_constructible_v<T, Args...>);
290//!
291//! /// @brief Participates in the resolution only if the index is within range
292//! /// and if the type can be constructor from Args. Corresponds to (7) of
293//! /// std::variant.
294//! template <std::size_t I, typename... Args, typename T = type_from_index_t<I>,
295//! typename = std::enable_if_t<std::is_constructible_v<T, Args...>>>
296//! explicit variant(
297//! [[maybe_unused]] std::in_place_index_t<I> index,
298//! Args &&...args) noexcept(std::is_nothrow_constructible_v<T, Args...>);
299//!
300//! /// @brief Converts the std::variant to our variant. Participates only in
301//! /// the resolution if all types in Ts are copy constructable.
302//! template <typename... Rs, typename = std::enable_if_t<
303//! all_same_v<Rs...> && all_copy_constructible_v>>
304//! variant(const std::variant<Rs...> &other);
305//!
306//! /// @brief Converts the std::variant to our variant. Participates only in
307//! /// the resolution if all types in Ts are move constructable.
308//! template <typename... Rs, typename = std::enable_if_t<
309//! all_same_v<Rs...> && all_move_constructible_v>>
310//! variant(std::variant<Rs...> &&other);
311//!
312//! /// @brief Copy assignment. Statically fails if not every type in Ts is copy
313//! /// constructable. Corresponds to (1) assignment of std::variant.
314//! variant_base &operator=(const variant_base &other);
315//!
316//! /// @brief Deleted move assignment. Same as for the move constructor.
317//! /// Would correspond to (2) assignment of std::variant.
318//! variant_base &operator=(variant_base &&other) = delete;
319//!
320//! /// @brief Converting assignment. Corresponds to (3) assignment of
321//! /// std::variant.
322//! template <typename T, typename = std::enable_if_t<
323//! is_unique_v<T> && std::is_constructible_v<T &&, T>>>
324//! variant_base &operator=(T &&other);
325//!
326//! /// @brief Converting assignment from std::variant. Participates only in the
327//! /// resolution if all types in Ts are copy constructable.
328//! template <typename... Rs, typename = std::enable_if_t<
329//! all_same_v<Rs...> && all_copy_constructible_v>>
330//! variant_base &operator=(const std::variant<Rs...> &other);
331//!
332//! /// @brief Converting assignment from std::variant. Participates only in the
333//! /// resolution if all types in Ts are move constructable.
334//! template <typename... Rs, typename = std::enable_if_t<
335//! all_same_v<Rs...> && all_move_constructible_v>>
336//! variant_base &operator=(std::variant<Rs...> &&other);
337//!
338//! /// @brief Emplace function. Participates in the resolution only if T is
339//! /// unique in Ts and if T can be constructed from Args. Offers strong
340//! /// exception guarantee. Corresponds to the (1) emplace function of
341//! /// std::variant.
342//! template <typename T, typename... Args,
343//! typename = std::enable_if_t<is_unique_v<T>>,
344//! typename = std::enable_if_t<std::is_constructible_v<T, Args...>>>
345//! T &emplace(Args &&...args);
346//!
347//! /// @brief Emplace function. Participates in the resolution only if T can be
348//! /// constructed from Args. Offers strong exception guarantee. Corresponds to
349//! /// the (2) emplace function of std::variant.
350//! ///
351//! /// The std::variant can have no valid state if the type throws during the
352//! /// construction. This is represented by the `valueless_by_exception` flag.
353//! /// The same approach is also used in absl::variant [2].
354//! /// In our case we can't accept valueless enums since we can't represent
355//! /// this in Rust. We must therefore provide a strong exception guarantee for
356//! /// all operations using `emplace`. Two famous implementations of never
357//! /// valueless variants are Boost/variant [3] and Boost/variant2 [4].
358//! /// Boost/variant2 uses two memory buffers - which would be not compatible
359//! /// with Rust Enum's memory layout. The Boost/variant backs up the old
360//! /// object and calls its d'tor before constructing the new object; It then
361//! /// copies the old data back to the buffer if the construction fails - which
362//! /// might contain garbage (since) the d'tor was already called.
363//! ///
364//! ///
365//! /// We take a similar approach to Boost/variant. Assuming that constructing
366//! /// or moving the new type can throw, we backup the old data, try to
367//! /// construct the new object in the final buffer, swap the buffers, such
368//! /// that the old object is back in its original place, destroy it and move
369//! /// the new object from the old buffer back to the final place.
370//! ///
371//! /// Sources
372//! ///
373//! /// [1]
374//! /// https://en.cppreference.com/w/cpp/utility/variant/valueless_by_exception
375//! /// [2]
376//! /// https://github.com/abseil/abseil-cpp/blob/master/absl/types/variant.h
377//! /// [3]
378//! /// https://www.boost.org/doc/libs/1_84_0/libs/variant2/doc/html/variant2.html
379//! /// [4]
380//! /// https://www.boost.org/doc/libs/1_84_0/doc/html/variant/design.html#variant.design.never-empty
381//! template <std::size_t I, typename... Args, typename T = type_from_index_t<I>,
382//! typename = std::enable_if_t<std::is_constructible_v<T, Args...>>>
383//! T &emplace(Args &&...args);
384//!
385//! constexpr std::size_t index() const noexcept;
386//!
387//! void swap(variant_base &other);
388//!
389//!
390//! struct bad_rust_variant_access : std::runtime_error {
391//! bad_rust_variant_access(std::size_t index)
392//! : std::runtime_error{"The index should be " + std::to_string(index)} {}
393//! };
394//! }
395//!
396//! } // namespace enm
397//! } // namespace rust
398//! ```
399//!
400//! ### `rust::enm::Optional`
401//!
402//! Simplicited declaration
403//!
404//! ```c++
405//!
406//! namespace rust {
407//! namespace enm {
408//!
409//! struct bad_rust_optional_access : std::runtime_error {
410//! bad_rust_optional_access() : std::runtime_error{"Optional has no value"} {}
411//! };
412//!
413//! template <typename T> struct optional : public variant<monostate, T> {
414//! using base = variant<monostate, T>;
415//!
416//! optional() : base(monostate{}) {};
417//! optional(std::nullopt_t) : base(monostate{}) {};
418//! optional(const optional &) = default;
419//! optional(optional &&) = delete;
420//!
421//! using base::base;
422//! using base::operator=;
423//!
424//! using Some = T;
425//! using None = monostate;
426//!
427//! constexpr explicit operator bool() { return this->m_Index == 1; }
428//! constexpr bool has_value() const noexcept { return this->m_Index == 1; }
429//! constexpr bool is_some() const noexcept { return this->m_Index == 1; }
430//! constexpr bool is_none() const noexcept { return this->m_Index == 0; }
431//!
432//! /// @brief returns the contined value
433//! ///
434//! /// if `*this` contains a value returns a refrence. Otherwise throws
435//! /// bad_rust_optional_access
436//! /// @throws bad_rust_optional_access
437//! constexpr T &value() &;
438//!
439//! /// @brief returns the contined value
440//! ///
441//! /// if `*this` contains a value returns a refrence. Otherwise throws
442//! /// bad_rust_optional_access
443//! /// @throws bad_rust_optional_access
444//! constexpr const T &value() const &;
445//!
446//! /// @brief resets the optional to an empty state
447//! ///
448//! /// if `has_value()` is true first calls the deconstructor
449//! constexpr void reset() noexcept;
450//!
451//! constexpr const T *operator->() const noexcept;
452//! constexpr T *operator->() noexcept;
453//!
454//! constexpr const T &operator*() const & noexcept;
455//! constexpr T &operator*() & noexcept;
456//!
457//! template <class U = std::remove_cv_t<T>>
458//! constexpr T value_or(U &&default_value) const &;
459//!
460//! template <class F> constexpr auto and_then(F &&f) &;
461//! template <class F> constexpr auto and_then(F &&f) const &;
462//! template <class F> constexpr auto transform(F &&f) &;
463//!
464//! template <class F> constexpr auto transform(F &&f) const &;
465//!
466//! template <class F> constexpr T &or_else(F &&f) &;
467//! template <class F> constexpr const T &or_else(F &&f) const &;
468//! };
469//!
470//!
471//! /// Compares two optional objects
472//! template <class T, class U>
473//! inline constexpr bool operator==(const optional<T> &lhs,
474//! const optional<U> &rhs);
475//! template <class T, class U>
476//! inline constexpr bool operator!=(const optional<T> &lhs,
477//! const optional<U> &rhs);
478//! template <class T, class U>
479//! inline constexpr bool operator<(const optional<T> &lhs,
480//! const optional<U> &rhs)
481//! // ... and many more compare operators
482//! ```
483//!
484//! ### `rust::enm::expected`
485//!
486//! Simplicited declaration
487//!
488//! ```c++
489//!
490//! namespace rust {
491//! namespace enm {
492//!
493//! template <typename E> struct bad_rust_expected_access : std::runtime_error {
494//! bad_rust_expected_access(const E &err)
495//! : std::runtime_error{"Expected is the unexpected value"}, error(err) {}
496//! E error;
497//! };
498//!
499//! template <typename T, typename E> struct expected : public variant<T, E> {
500//! using base = variant<T, E>;
501//!
502//! expected() = delete;
503//! expected(const expected &) = default;
504//! expected(expected &&) = delete;
505//!
506//! using base::base;
507//! using base::operator=;
508//!
509//! using Ok = T;
510//! using Err = E;
511//!
512//! constexpr explicit operator bool() { return this->m_Index == 0; }
513//! constexpr bool has_value() const noexcept { return this->m_Index == 0; }
514//!
515//! /// @brief returns the expected value
516//! ///
517//! /// @throws bad_rust_expected_access<E> with a copy of the unexpected value
518//! constexpr T &value() &;
519//!
520//! /// @brief returns the expected value
521//! ///
522//! /// @throws bad_rust_expected_access<E> with a copy of the unexpected value
523//! constexpr const T &value() const &;
524//!
525//! /// @brief returns the unexpected value
526//! ///
527//! /// if has_value() is `true`, the behavior is undefined
528//! constexpr E &error() &;
529//!
530//! /// @brief returns the unexpected value
531//! ///
532//! /// if has_value() is `true`, the behavior is undefined
533//! constexpr const E &error() const &;
534//!
535//! constexpr const T *operator->() const noexcept;
536//! constexpr T *operator->() noexcept;
537//!
538//! constexpr const T &operator*() const & noexcept;
539//! constexpr T &operator*() & noexcept;
540//!
541//! /// @brief Returns the expected value if it exists, otherwise returns
542//! /// `default_value`
543//! template <class U = std::remove_cv_t<T>>
544//! constexpr T value_or(U &&default_value) const &;
545//!
546//! /// @brief Returns the unexpected value if it exists, otherwise returns
547//! /// `default_value`
548//! template <class G = E> constexpr E error_or(G &&default_value) const &;
549//!
550//! template <class F> constexpr auto and_then(F &&f) &;
551//! template <class F> constexpr auto and_then(F &&f) const &;
552//!
553//! template <class F> constexpr auto transform(F &&f) &;
554//! template <class F> constexpr auto transform(F &&f) const &;
555//!
556//! template <class F> constexpr T &or_else(F &&f) &;
557//! template <class F> constexpr const T &or_else(F &&f) const &;
558//!
559//! template <class F> constexpr auto transform_error(F &&f) &;
560//! template <class F> constexpr auto transform_error(F &&f) const &;
561//!
562//! };
563//!
564//! } // namespace enm
565//! } // namespace rust
566//! ```
567//!
568
569pub use cxx_enumext_macro::extern_type;
570
571/// Private assert helpers
572pub mod private {
573
574 #[allow(dead_code)]
575 pub trait NotCxxExternType {
576 const IS_CXX_EXTERN_TYPE: bool = false;
577 }
578 impl<T: ?Sized> NotCxxExternType for T {}
579 pub struct IsCxxExternType<T: ?Sized>(std::marker::PhantomData<T>);
580 #[allow(dead_code)]
581 impl<T: ?Sized + ::cxx::ExternType> IsCxxExternType<T> {
582 pub const IS_CXX_EXTERN_TYPE: bool = true;
583 }
584
585 #[allow(dead_code)]
586 pub trait NotCxxExternTrivial {
587 const IS_CXX_EXTERN_TRIVIAL: bool = false;
588 }
589 impl<T: ?Sized> NotCxxExternTrivial for T {}
590 pub struct IsCxxExternTrivial<T: ?Sized>(std::marker::PhantomData<T>);
591 #[allow(dead_code)]
592 impl<T: ?Sized + ::cxx::ExternType<Kind = ::cxx::kind::Trivial>> IsCxxExternTrivial<T> {
593 pub const IS_CXX_EXTERN_TRIVIAL: bool = true;
594 }
595
596 #[allow(dead_code)]
597 pub trait NotCxxExternOpaque {
598 const IS_CXX_EXTERN_OPAQUE: bool = false;
599 }
600 impl<T: ?Sized> NotCxxExternOpaque for T {}
601 pub struct IsCxxExternOpaque<T: ?Sized>(std::marker::PhantomData<T>);
602 #[allow(dead_code)]
603 impl<T: ?Sized + ::cxx::ExternType<Kind = ::cxx::kind::Trivial>> IsCxxExternOpaque<T> {
604 pub const IS_CXX_EXTERN_OPAQUE: bool = true;
605 }
606
607 #[allow(dead_code)]
608 pub trait NotCxxImplVec {
609 const IS_CXX_IMPL_VEC: bool = false;
610 }
611 impl<T: ?Sized> NotCxxImplVec for T {}
612 pub struct IsCxxImplVec<T: ?Sized>(std::marker::PhantomData<T>);
613 #[allow(dead_code)]
614 impl<T: ?Sized + ::cxx::private::ImplVec> IsCxxImplVec<T> {
615 pub const IS_CXX_IMPL_VEC: bool = true;
616 }
617
618 #[allow(dead_code)]
619 pub trait NotCxxImplBox {
620 const IS_CXX_IMPL_BOX: bool = false;
621 }
622 impl<T: ?Sized> NotCxxImplBox for T {}
623 pub struct IsCxxImplBox<T: ?Sized>(std::marker::PhantomData<T>);
624 #[allow(dead_code)]
625 impl<T: ?Sized + ::cxx::private::ImplBox> IsCxxImplBox<T> {
626 pub const IS_CXX_IMPL_BOX: bool = true;
627 }
628}