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