cxx/
shared_ptr.rs

1use crate::extern_type::ExternType;
2use crate::fmt::display;
3use crate::kind::Trivial;
4use crate::string::CxxString;
5use crate::unique_ptr::{UniquePtr, UniquePtrTarget};
6use crate::weak_ptr::{WeakPtr, WeakPtrTarget};
7use core::cmp::Ordering;
8use core::ffi::c_void;
9use core::fmt::{self, Debug, Display};
10use core::hash::{Hash, Hasher};
11use core::marker::PhantomData;
12use core::mem::MaybeUninit;
13use core::ops::Deref;
14use core::pin::Pin;
15
16/// Binding to C++ `std::shared_ptr<T>`.
17///
18/// <div class="warning">
19///
20/// **WARNING:** Unlike Rust's `Arc<T>`, a C++ shared pointer manipulates
21/// pointers to 2 separate objects in general.
22///
23/// 1. One is the **managed** pointer, and its identity is associated with
24///    shared ownership of a strong and weak count shared by other SharedPtr and
25///    WeakPtr instances having the same managed pointer.
26///
27/// 2. The other is the **stored** pointer, which is commonly either the same as
28///    the managed pointer, or is a pointer into some member of the managed
29///    object, but can be any unrelated pointer in general.
30///
31/// The managed pointer is the one passed to a deleter upon the strong count
32/// reaching zero, but the stored pointer is the one accessed by deref
33/// operations and methods such as `is_null`.
34///
35/// A shared pointer is considered **empty** if the strong count is zero,
36/// meaning the managed pointer has been deleted or is about to be deleted. A
37/// shared pointer is considered **null** if the stored pointer is the null
38/// pointer. All combinations are possible. To be explicit, a shared pointer can
39/// be nonempty and nonnull, or nonempty and null, or empty and nonnull, or
40/// empty and null. In general all of these cases need to be considered when
41/// handling a SharedPtr.
42///
43/// </div>
44#[repr(C)]
45pub struct SharedPtr<T>
46where
47    T: SharedPtrTarget,
48{
49    repr: [MaybeUninit<*mut c_void>; 2],
50    ty: PhantomData<T>,
51}
52
53impl<T> SharedPtr<T>
54where
55    T: SharedPtrTarget,
56{
57    /// Makes a new SharedPtr that is both **empty** and **null**.
58    ///
59    /// Matches the behavior of default-constructing a std::shared\_ptr.
60    pub fn null() -> Self {
61        let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
62        let new = shared_ptr.as_mut_ptr().cast();
63        unsafe {
64            T::__null(new);
65            shared_ptr.assume_init()
66        }
67    }
68
69    /// Allocates memory on the heap and makes a SharedPtr owner for it.
70    ///
71    /// The shared pointer will be **nonempty** and **nonnull**.
72    pub fn new(value: T) -> Self
73    where
74        T: ExternType<Kind = Trivial>,
75    {
76        let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
77        let new = shared_ptr.as_mut_ptr().cast();
78        unsafe {
79            T::__new(value, new);
80            shared_ptr.assume_init()
81        }
82    }
83
84    /// Creates a shared pointer from a C++ heap-allocated pointer.
85    ///
86    /// Matches the behavior of std::shared\_ptr's constructor `explicit shared_ptr(T*)`.
87    ///
88    /// The SharedPtr gains ownership of the pointer and will call
89    /// `std::default_delete` on it when the refcount goes to zero.
90    ///
91    /// The object pointed to by the input pointer is not relocated by this
92    /// operation, so any pointers into this data structure elsewhere in the
93    /// program continue to be valid.
94    ///
95    /// The resulting shared pointer is **nonempty** regardless of whether the
96    /// input pointer is null, but may be either **null** or **nonnull**.
97    ///
98    /// # Panics
99    ///
100    /// Panics if `T` is an incomplete type (including `void`) or is not
101    /// destructible.
102    ///
103    /// # Safety
104    ///
105    /// Pointer must either be null or point to a valid instance of T
106    /// heap-allocated in C++ by `new`.
107    #[track_caller]
108    pub unsafe fn from_raw(raw: *mut T) -> Self {
109        let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
110        let new = shared_ptr.as_mut_ptr().cast();
111        unsafe {
112            T::__raw(new, raw);
113            shared_ptr.assume_init()
114        }
115    }
116
117    /// Checks whether the SharedPtr holds a null stored pointer.
118    ///
119    /// This is the opposite of [std::shared_ptr\<T\>::operator bool](https://en.cppreference.com/w/cpp/memory/shared_ptr/operator_bool).
120    ///
121    /// <div class="warning">
122    ///
123    /// This method is unrelated to the state of the reference count. It is
124    /// possible to have a SharedPtr that is nonnull but empty (has a refcount
125    /// of 0), typically from having been constructed using the alias
126    /// constructors in C++. Inversely, it is also possible to be null and
127    /// nonempty.
128    ///
129    /// </div>
130    pub fn is_null(&self) -> bool {
131        let this = self as *const Self as *const c_void;
132        let ptr = unsafe { T::__get(this) };
133        ptr.is_null()
134    }
135
136    /// Returns a reference to the object pointed to by the stored pointer if
137    /// nonnull, otherwise None.
138    ///
139    /// <div class="warning">
140    ///
141    /// The shared pointer's managed object may or may not already have been
142    /// destroyed.
143    ///
144    /// </div>
145    pub fn as_ref(&self) -> Option<&T> {
146        let ptr = self.as_ptr();
147        unsafe { ptr.as_ref() }
148    }
149
150    /// Returns a mutable pinned reference to the object pointed to by the
151    /// stored pointer.
152    ///
153    /// <div class="warning">
154    ///
155    /// The shared pointer's managed object may or may not already have been
156    /// destroyed.
157    ///
158    /// </div>
159    ///
160    /// # Panics
161    ///
162    /// Panics if the SharedPtr holds a null stored pointer.
163    ///
164    /// # Safety
165    ///
166    /// This method makes no attempt to ascertain the state of the reference
167    /// count. In particular, unlike `Arc::get_mut`, we do not enforce absence
168    /// of other SharedPtr and WeakPtr referring to the same data as this one.
169    /// As always, it is Undefined Behavior to have simultaneous references to
170    /// the same value while a Rust exclusive reference to it exists anywhere in
171    /// the program.
172    ///
173    /// For the special case of CXX [opaque C++ types], this method can be used
174    /// to safely call thread-safe non-const member functions on a C++ object
175    /// without regard for whether the reference is exclusive. This capability
176    /// applies only to opaque types `extern "C++" { type T; }`. It does not
177    /// apply to extern types defined with a non-opaque Rust representation
178    /// `extern "C++" { type T = ...; }`.
179    ///
180    /// [opaque C++ types]: https://cxx.rs/extern-c++.html#opaque-c-types
181    pub unsafe fn pin_mut_unchecked(&mut self) -> Pin<&mut T> {
182        let ptr = self.as_mut_ptr();
183        match unsafe { ptr.as_mut() } {
184            Some(target) => unsafe { Pin::new_unchecked(target) },
185            None => panic!(
186                "called pin_mut_unchecked on a null SharedPtr<{}>",
187                display(T::__typename),
188            ),
189        }
190    }
191
192    /// Returns the SharedPtr's stored pointer as a raw const pointer.
193    pub fn as_ptr(&self) -> *const T {
194        let this = self as *const Self as *const c_void;
195        unsafe { T::__get(this) }
196    }
197
198    /// Returns the SharedPtr's stored pointer as a raw mutable pointer.
199    ///
200    /// As with [std::shared_ptr\<T\>::get](https://en.cppreference.com/w/cpp/memory/shared_ptr/get),
201    /// this doesn't require that you hold an exclusive reference to the
202    /// SharedPtr. This differs from Rust norms, so extra care should be taken
203    /// in the way the pointer is used.
204    pub fn as_mut_ptr(&self) -> *mut T {
205        self.as_ptr() as *mut T
206    }
207
208    /// Constructs new WeakPtr as a non-owning reference to the object managed
209    /// by `self`. If `self` manages no object, the WeakPtr manages no object
210    /// too.
211    ///
212    /// 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).
213    pub fn downgrade(&self) -> WeakPtr<T>
214    where
215        T: WeakPtrTarget,
216    {
217        let this = self as *const Self as *const c_void;
218        let mut weak_ptr = MaybeUninit::<WeakPtr<T>>::uninit();
219        let new = weak_ptr.as_mut_ptr().cast();
220        unsafe {
221            T::__downgrade(this, new);
222            weak_ptr.assume_init()
223        }
224    }
225}
226
227unsafe impl<T> Send for SharedPtr<T> where T: Send + Sync + SharedPtrTarget {}
228unsafe impl<T> Sync for SharedPtr<T> where T: Send + Sync + SharedPtrTarget {}
229
230impl<T> Clone for SharedPtr<T>
231where
232    T: SharedPtrTarget,
233{
234    fn clone(&self) -> Self {
235        let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
236        let new = shared_ptr.as_mut_ptr().cast();
237        let this = self as *const Self as *mut c_void;
238        unsafe {
239            T::__clone(this, new);
240            shared_ptr.assume_init()
241        }
242    }
243}
244
245// SharedPtr is not a self-referential type and is safe to move out of a Pin,
246// regardless whether the pointer's target is Unpin.
247impl<T> Unpin for SharedPtr<T> where T: SharedPtrTarget {}
248
249impl<T> Drop for SharedPtr<T>
250where
251    T: SharedPtrTarget,
252{
253    fn drop(&mut self) {
254        let this = self as *mut Self as *mut c_void;
255        unsafe { T::__drop(this) }
256    }
257}
258
259impl<T> Deref for SharedPtr<T>
260where
261    T: SharedPtrTarget,
262{
263    type Target = T;
264
265    fn deref(&self) -> &Self::Target {
266        match self.as_ref() {
267            Some(target) => target,
268            None => panic!(
269                "called deref on a null SharedPtr<{}>",
270                display(T::__typename),
271            ),
272        }
273    }
274}
275
276impl<T> Debug for SharedPtr<T>
277where
278    T: Debug + SharedPtrTarget,
279{
280    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
281        match self.as_ref() {
282            None => formatter.write_str("nullptr"),
283            Some(value) => Debug::fmt(value, formatter),
284        }
285    }
286}
287
288impl<T> Display for SharedPtr<T>
289where
290    T: Display + SharedPtrTarget,
291{
292    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
293        match self.as_ref() {
294            None => formatter.write_str("nullptr"),
295            Some(value) => Display::fmt(value, formatter),
296        }
297    }
298}
299
300impl<T> PartialEq for SharedPtr<T>
301where
302    T: PartialEq + SharedPtrTarget,
303{
304    fn eq(&self, other: &Self) -> bool {
305        self.as_ref() == other.as_ref()
306    }
307}
308
309impl<T> Eq for SharedPtr<T> where T: Eq + SharedPtrTarget {}
310
311impl<T> PartialOrd for SharedPtr<T>
312where
313    T: PartialOrd + SharedPtrTarget,
314{
315    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
316        PartialOrd::partial_cmp(&self.as_ref(), &other.as_ref())
317    }
318}
319
320impl<T> Ord for SharedPtr<T>
321where
322    T: Ord + SharedPtrTarget,
323{
324    fn cmp(&self, other: &Self) -> Ordering {
325        Ord::cmp(&self.as_ref(), &other.as_ref())
326    }
327}
328
329impl<T> Hash for SharedPtr<T>
330where
331    T: Hash + SharedPtrTarget,
332{
333    fn hash<H>(&self, hasher: &mut H)
334    where
335        H: Hasher,
336    {
337        self.as_ref().hash(hasher);
338    }
339}
340
341impl<T> From<UniquePtr<T>> for SharedPtr<T>
342where
343    T: UniquePtrTarget + SharedPtrTarget,
344{
345    fn from(unique: UniquePtr<T>) -> Self {
346        unsafe { SharedPtr::from_raw(UniquePtr::into_raw(unique)) }
347    }
348}
349
350/// Trait bound for types which may be used as the `T` inside of a
351/// `SharedPtr<T>` in generic code.
352///
353/// This trait has no publicly callable or implementable methods. Implementing
354/// it outside of the CXX codebase is not supported.
355///
356/// # Example
357///
358/// A bound `T: SharedPtrTarget` may be necessary when manipulating
359/// [`SharedPtr`] in generic code.
360///
361/// ```
362/// use cxx::memory::{SharedPtr, SharedPtrTarget};
363/// use std::fmt::Display;
364///
365/// pub fn take_generic_ptr<T>(ptr: SharedPtr<T>)
366/// where
367///     T: SharedPtrTarget + Display,
368/// {
369///     println!("the shared_ptr points to: {}", *ptr);
370/// }
371/// ```
372///
373/// Writing the same generic function without a `SharedPtrTarget` trait bound
374/// would not compile.
375pub unsafe trait SharedPtrTarget {
376    #[doc(hidden)]
377    fn __typename(f: &mut fmt::Formatter) -> fmt::Result;
378    #[doc(hidden)]
379    unsafe fn __null(new: *mut c_void);
380    #[doc(hidden)]
381    unsafe fn __new(value: Self, new: *mut c_void)
382    where
383        Self: Sized,
384    {
385        // Opaque C types do not get this method because they can never exist by
386        // value on the Rust side of the bridge.
387        let _ = value;
388        let _ = new;
389        unreachable!()
390    }
391    #[doc(hidden)]
392    unsafe fn __raw(new: *mut c_void, raw: *mut Self);
393    #[doc(hidden)]
394    unsafe fn __clone(this: *const c_void, new: *mut c_void);
395    #[doc(hidden)]
396    unsafe fn __get(this: *const c_void) -> *const Self;
397    #[doc(hidden)]
398    unsafe fn __drop(this: *mut c_void);
399}
400
401macro_rules! impl_shared_ptr_target {
402    ($segment:expr, $name:expr, $ty:ty) => {
403        unsafe impl SharedPtrTarget for $ty {
404            fn __typename(f: &mut fmt::Formatter) -> fmt::Result {
405                f.write_str($name)
406            }
407            unsafe fn __null(new: *mut c_void) {
408                extern "C" {
409                    #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$null")]
410                    fn __null(new: *mut c_void);
411                }
412                unsafe { __null(new) }
413            }
414            unsafe fn __new(value: Self, new: *mut c_void) {
415                extern "C" {
416                    #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$uninit")]
417                    fn __uninit(new: *mut c_void) -> *mut c_void;
418                }
419                unsafe { __uninit(new).cast::<$ty>().write(value) }
420            }
421            unsafe fn __raw(new: *mut c_void, raw: *mut Self) {
422                extern "C" {
423                    #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$raw")]
424                    fn __raw(new: *mut c_void, raw: *mut c_void);
425                }
426                unsafe { __raw(new, raw as *mut c_void) }
427            }
428            unsafe fn __clone(this: *const c_void, new: *mut c_void) {
429                extern "C" {
430                    #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$clone")]
431                    fn __clone(this: *const c_void, new: *mut c_void);
432                }
433                unsafe { __clone(this, new) }
434            }
435            unsafe fn __get(this: *const c_void) -> *const Self {
436                extern "C" {
437                    #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$get")]
438                    fn __get(this: *const c_void) -> *const c_void;
439                }
440                unsafe { __get(this) }.cast()
441            }
442            unsafe fn __drop(this: *mut c_void) {
443                extern "C" {
444                    #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$drop")]
445                    fn __drop(this: *mut c_void);
446                }
447                unsafe { __drop(this) }
448            }
449        }
450    };
451}
452
453macro_rules! impl_shared_ptr_target_for_primitive {
454    ($ty:ident) => {
455        impl_shared_ptr_target!(stringify!($ty), stringify!($ty), $ty);
456    };
457}
458
459impl_shared_ptr_target_for_primitive!(bool);
460impl_shared_ptr_target_for_primitive!(u8);
461impl_shared_ptr_target_for_primitive!(u16);
462impl_shared_ptr_target_for_primitive!(u32);
463impl_shared_ptr_target_for_primitive!(u64);
464impl_shared_ptr_target_for_primitive!(usize);
465impl_shared_ptr_target_for_primitive!(i8);
466impl_shared_ptr_target_for_primitive!(i16);
467impl_shared_ptr_target_for_primitive!(i32);
468impl_shared_ptr_target_for_primitive!(i64);
469impl_shared_ptr_target_for_primitive!(isize);
470impl_shared_ptr_target_for_primitive!(f32);
471impl_shared_ptr_target_for_primitive!(f64);
472
473impl_shared_ptr_target!("string", "CxxString", CxxString);