cxx/
shared_ptr.rs

1use crate::fmt::display;
2use crate::kind::Trivial;
3use crate::string::CxxString;
4use crate::weak_ptr::{WeakPtr, WeakPtrTarget};
5use crate::ExternType;
6use core::ffi::c_void;
7use core::fmt::{self, Debug, Display};
8use core::marker::PhantomData;
9use core::mem::MaybeUninit;
10use core::ops::Deref;
11
12/// Binding to C++ `std::shared_ptr<T>`.
13#[repr(C)]
14pub struct SharedPtr<T>
15where
16    T: SharedPtrTarget,
17{
18    repr: [MaybeUninit<*mut c_void>; 2],
19    ty: PhantomData<T>,
20}
21
22impl<T> SharedPtr<T>
23where
24    T: SharedPtrTarget,
25{
26    /// Makes a new SharedPtr wrapping a null pointer.
27    ///
28    /// Matches the behavior of default-constructing a std::shared\_ptr.
29    pub fn null() -> Self {
30        let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
31        let new = shared_ptr.as_mut_ptr().cast();
32        unsafe {
33            T::__null(new);
34            shared_ptr.assume_init()
35        }
36    }
37
38    /// Allocates memory on the heap and makes a SharedPtr owner for it.
39    pub fn new(value: T) -> Self
40    where
41        T: ExternType<Kind = Trivial>,
42    {
43        let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
44        let new = shared_ptr.as_mut_ptr().cast();
45        unsafe {
46            T::__new(value, new);
47            shared_ptr.assume_init()
48        }
49    }
50
51    /// Checks whether the SharedPtr does not own an object.
52    ///
53    /// This is the opposite of [std::shared_ptr\<T\>::operator bool](https://en.cppreference.com/w/cpp/memory/shared_ptr/operator_bool).
54    pub fn is_null(&self) -> bool {
55        let this = self as *const Self as *const c_void;
56        let ptr = unsafe { T::__get(this) };
57        ptr.is_null()
58    }
59
60    /// Returns a reference to the object owned by this SharedPtr if any,
61    /// otherwise None.
62    pub fn as_ref(&self) -> Option<&T> {
63        let this = self as *const Self as *const c_void;
64        unsafe { T::__get(this).as_ref() }
65    }
66
67    /// Constructs new WeakPtr as a non-owning reference to the object managed
68    /// by `self`. If `self` manages no object, the WeakPtr manages no object
69    /// too.
70    ///
71    /// Matches the behavior of [std::weak_ptr\<T\>::weak_ptr(const std::shared_ptr\<T\> \&)](https://en.cppreference.com/w/cpp/memory/weak_ptr/weak_ptr).
72    pub fn downgrade(self: &SharedPtr<T>) -> WeakPtr<T>
73    where
74        T: WeakPtrTarget,
75    {
76        let this = self as *const Self as *const c_void;
77        let mut weak_ptr = MaybeUninit::<WeakPtr<T>>::uninit();
78        let new = weak_ptr.as_mut_ptr().cast();
79        unsafe {
80            T::__downgrade(this, new);
81            weak_ptr.assume_init()
82        }
83    }
84}
85
86unsafe impl<T> Send for SharedPtr<T> where T: Send + Sync + SharedPtrTarget {}
87unsafe impl<T> Sync for SharedPtr<T> where T: Send + Sync + SharedPtrTarget {}
88
89impl<T> Clone for SharedPtr<T>
90where
91    T: SharedPtrTarget,
92{
93    fn clone(&self) -> Self {
94        let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
95        let new = shared_ptr.as_mut_ptr().cast();
96        let this = self as *const Self as *mut c_void;
97        unsafe {
98            T::__clone(this, new);
99            shared_ptr.assume_init()
100        }
101    }
102}
103
104// SharedPtr is not a self-referential type and is safe to move out of a Pin,
105// regardless whether the pointer's target is Unpin.
106impl<T> Unpin for SharedPtr<T> where T: SharedPtrTarget {}
107
108impl<T> Drop for SharedPtr<T>
109where
110    T: SharedPtrTarget,
111{
112    fn drop(&mut self) {
113        let this = self as *mut Self as *mut c_void;
114        unsafe { T::__drop(this) }
115    }
116}
117
118impl<T> Deref for SharedPtr<T>
119where
120    T: SharedPtrTarget,
121{
122    type Target = T;
123
124    fn deref(&self) -> &Self::Target {
125        match self.as_ref() {
126            Some(target) => target,
127            None => panic!(
128                "called deref on a null SharedPtr<{}>",
129                display(T::__typename),
130            ),
131        }
132    }
133}
134
135impl<T> Debug for SharedPtr<T>
136where
137    T: Debug + SharedPtrTarget,
138{
139    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
140        match self.as_ref() {
141            None => formatter.write_str("nullptr"),
142            Some(value) => Debug::fmt(value, formatter),
143        }
144    }
145}
146
147impl<T> Display for SharedPtr<T>
148where
149    T: Display + SharedPtrTarget,
150{
151    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
152        match self.as_ref() {
153            None => formatter.write_str("nullptr"),
154            Some(value) => Display::fmt(value, formatter),
155        }
156    }
157}
158
159/// Trait bound for types which may be used as the `T` inside of a
160/// `SharedPtr<T>` in generic code.
161///
162/// This trait has no publicly callable or implementable methods. Implementing
163/// it outside of the CXX codebase is not supported.
164///
165/// # Example
166///
167/// A bound `T: SharedPtrTarget` may be necessary when manipulating
168/// [`SharedPtr`] in generic code.
169///
170/// ```
171/// use cxx::memory::{SharedPtr, SharedPtrTarget};
172/// use std::fmt::Display;
173///
174/// pub fn take_generic_ptr<T>(ptr: SharedPtr<T>)
175/// where
176///     T: SharedPtrTarget + Display,
177/// {
178///     println!("the shared_ptr points to: {}", *ptr);
179/// }
180/// ```
181///
182/// Writing the same generic function without a `SharedPtrTarget` trait bound
183/// would not compile.
184pub unsafe trait SharedPtrTarget {
185    #[doc(hidden)]
186    fn __typename(f: &mut fmt::Formatter) -> fmt::Result;
187    #[doc(hidden)]
188    unsafe fn __null(new: *mut c_void);
189    #[doc(hidden)]
190    unsafe fn __new(value: Self, new: *mut c_void)
191    where
192        Self: Sized,
193    {
194        // Opaque C types do not get this method because they can never exist by
195        // value on the Rust side of the bridge.
196        let _ = value;
197        let _ = new;
198        unreachable!()
199    }
200    #[doc(hidden)]
201    unsafe fn __clone(this: *const c_void, new: *mut c_void);
202    #[doc(hidden)]
203    unsafe fn __get(this: *const c_void) -> *const Self;
204    #[doc(hidden)]
205    unsafe fn __drop(this: *mut c_void);
206}
207
208macro_rules! impl_shared_ptr_target {
209    ($segment:expr, $name:expr, $ty:ty) => {
210        unsafe impl SharedPtrTarget for $ty {
211            fn __typename(f: &mut fmt::Formatter) -> fmt::Result {
212                f.write_str($name)
213            }
214            unsafe fn __null(new: *mut c_void) {
215                extern "C" {
216                    #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$null")]
217                    fn __null(new: *mut c_void);
218                }
219                unsafe { __null(new) }
220            }
221            unsafe fn __new(value: Self, new: *mut c_void) {
222                extern "C" {
223                    #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$uninit")]
224                    fn __uninit(new: *mut c_void) -> *mut c_void;
225                }
226                unsafe { __uninit(new).cast::<$ty>().write(value) }
227            }
228            unsafe fn __clone(this: *const c_void, new: *mut c_void) {
229                extern "C" {
230                    #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$clone")]
231                    fn __clone(this: *const c_void, new: *mut c_void);
232                }
233                unsafe { __clone(this, new) }
234            }
235            unsafe fn __get(this: *const c_void) -> *const Self {
236                extern "C" {
237                    #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$get")]
238                    fn __get(this: *const c_void) -> *const c_void;
239                }
240                unsafe { __get(this) }.cast()
241            }
242            unsafe fn __drop(this: *mut c_void) {
243                extern "C" {
244                    #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$drop")]
245                    fn __drop(this: *mut c_void);
246                }
247                unsafe { __drop(this) }
248            }
249        }
250    };
251}
252
253macro_rules! impl_shared_ptr_target_for_primitive {
254    ($ty:ident) => {
255        impl_shared_ptr_target!(stringify!($ty), stringify!($ty), $ty);
256    };
257}
258
259impl_shared_ptr_target_for_primitive!(bool);
260impl_shared_ptr_target_for_primitive!(u8);
261impl_shared_ptr_target_for_primitive!(u16);
262impl_shared_ptr_target_for_primitive!(u32);
263impl_shared_ptr_target_for_primitive!(u64);
264impl_shared_ptr_target_for_primitive!(usize);
265impl_shared_ptr_target_for_primitive!(i8);
266impl_shared_ptr_target_for_primitive!(i16);
267impl_shared_ptr_target_for_primitive!(i32);
268impl_shared_ptr_target_for_primitive!(i64);
269impl_shared_ptr_target_for_primitive!(isize);
270impl_shared_ptr_target_for_primitive!(f32);
271impl_shared_ptr_target_for_primitive!(f64);
272
273impl_shared_ptr_target!("string", "CxxString", CxxString);