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 /// # Safety
99 ///
100 /// Pointer must either be null or point to a valid instance of T
101 /// heap-allocated in C++ by `new`.
102 pub unsafe fn from_raw(raw: *mut T) -> Self {
103 let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
104 let new = shared_ptr.as_mut_ptr().cast();
105 unsafe {
106 T::__raw(new, raw);
107 shared_ptr.assume_init()
108 }
109 }
110
111 /// Checks whether the SharedPtr holds a null stored pointer.
112 ///
113 /// This is the opposite of [std::shared_ptr\<T\>::operator bool](https://en.cppreference.com/w/cpp/memory/shared_ptr/operator_bool).
114 ///
115 /// <div class="warning">
116 ///
117 /// This method is unrelated to the state of the reference count. It is
118 /// possible to have a SharedPtr that is nonnull but empty (has a refcount
119 /// of 0), typically from having been constructed using the alias
120 /// constructors in C++. Inversely, it is also possible to be null and
121 /// nonempty.
122 ///
123 /// </div>
124 pub fn is_null(&self) -> bool {
125 let this = self as *const Self as *const c_void;
126 let ptr = unsafe { T::__get(this) };
127 ptr.is_null()
128 }
129
130 /// Returns a reference to the object pointed to by the stored pointer if
131 /// nonnull, otherwise None.
132 ///
133 /// <div class="warning">
134 ///
135 /// The shared pointer's managed object may or may not already have been
136 /// destroyed.
137 ///
138 /// </div>
139 pub fn as_ref(&self) -> Option<&T> {
140 let ptr = self.as_ptr();
141 unsafe { ptr.as_ref() }
142 }
143
144 /// Returns a mutable pinned reference to the object pointed to by the
145 /// stored pointer.
146 ///
147 /// <div class="warning">
148 ///
149 /// The shared pointer's managed object may or may not already have been
150 /// destroyed.
151 ///
152 /// </div>
153 ///
154 /// # Panics
155 ///
156 /// Panics if the SharedPtr holds a null stored pointer.
157 ///
158 /// # Safety
159 ///
160 /// This method makes no attempt to ascertain the state of the reference
161 /// count. In particular, unlike `Arc::get_mut`, we do not enforce absence
162 /// of other SharedPtr and WeakPtr referring to the same data as this one.
163 /// As always, it is Undefined Behavior to have simultaneous references to
164 /// the same value while a Rust exclusive reference to it exists anywhere in
165 /// the program.
166 ///
167 /// For the special case of CXX [opaque C++ types], this method can be used
168 /// to safely call thread-safe non-const member functions on a C++ object
169 /// without regard for whether the reference is exclusive. This capability
170 /// applies only to opaque types `extern "C++" { type T; }`. It does not
171 /// apply to extern types defined with a non-opaque Rust representation
172 /// `extern "C++" { type T = ...; }`.
173 ///
174 /// [opaque C++ types]: https://cxx.rs/extern-c++.html#opaque-c-types
175 pub unsafe fn pin_mut_unchecked(&mut self) -> Pin<&mut T> {
176 let ptr = self.as_mut_ptr();
177 match unsafe { ptr.as_mut() } {
178 Some(target) => unsafe { Pin::new_unchecked(target) },
179 None => panic!(
180 "called pin_mut_unchecked on a null SharedPtr<{}>",
181 display(T::__typename),
182 ),
183 }
184 }
185
186 /// Returns the SharedPtr's stored pointer as a raw const pointer.
187 pub fn as_ptr(&self) -> *const T {
188 let this = self as *const Self as *const c_void;
189 unsafe { T::__get(this) }
190 }
191
192 /// Returns the SharedPtr's stored pointer as a raw mutable pointer.
193 ///
194 /// As with [std::shared_ptr\<T\>::get](https://en.cppreference.com/w/cpp/memory/shared_ptr/get),
195 /// this doesn't require that you hold an exclusive reference to the
196 /// SharedPtr. This differs from Rust norms, so extra care should be taken
197 /// in the way the pointer is used.
198 pub fn as_mut_ptr(&self) -> *mut T {
199 self.as_ptr() as *mut T
200 }
201
202 /// Constructs new WeakPtr as a non-owning reference to the object managed
203 /// by `self`. If `self` manages no object, the WeakPtr manages no object
204 /// too.
205 ///
206 /// 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).
207 pub fn downgrade(&self) -> WeakPtr<T>
208 where
209 T: WeakPtrTarget,
210 {
211 let this = self as *const Self as *const c_void;
212 let mut weak_ptr = MaybeUninit::<WeakPtr<T>>::uninit();
213 let new = weak_ptr.as_mut_ptr().cast();
214 unsafe {
215 T::__downgrade(this, new);
216 weak_ptr.assume_init()
217 }
218 }
219}
220
221unsafe impl<T> Send for SharedPtr<T> where T: Send + Sync + SharedPtrTarget {}
222unsafe impl<T> Sync for SharedPtr<T> where T: Send + Sync + SharedPtrTarget {}
223
224impl<T> Clone for SharedPtr<T>
225where
226 T: SharedPtrTarget,
227{
228 fn clone(&self) -> Self {
229 let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
230 let new = shared_ptr.as_mut_ptr().cast();
231 let this = self as *const Self as *mut c_void;
232 unsafe {
233 T::__clone(this, new);
234 shared_ptr.assume_init()
235 }
236 }
237}
238
239// SharedPtr is not a self-referential type and is safe to move out of a Pin,
240// regardless whether the pointer's target is Unpin.
241impl<T> Unpin for SharedPtr<T> where T: SharedPtrTarget {}
242
243impl<T> Drop for SharedPtr<T>
244where
245 T: SharedPtrTarget,
246{
247 fn drop(&mut self) {
248 let this = self as *mut Self as *mut c_void;
249 unsafe { T::__drop(this) }
250 }
251}
252
253impl<T> Deref for SharedPtr<T>
254where
255 T: SharedPtrTarget,
256{
257 type Target = T;
258
259 fn deref(&self) -> &Self::Target {
260 match self.as_ref() {
261 Some(target) => target,
262 None => panic!(
263 "called deref on a null SharedPtr<{}>",
264 display(T::__typename),
265 ),
266 }
267 }
268}
269
270impl<T> Debug for SharedPtr<T>
271where
272 T: Debug + SharedPtrTarget,
273{
274 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
275 match self.as_ref() {
276 None => formatter.write_str("nullptr"),
277 Some(value) => Debug::fmt(value, formatter),
278 }
279 }
280}
281
282impl<T> Display for SharedPtr<T>
283where
284 T: Display + SharedPtrTarget,
285{
286 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
287 match self.as_ref() {
288 None => formatter.write_str("nullptr"),
289 Some(value) => Display::fmt(value, formatter),
290 }
291 }
292}
293
294impl<T> PartialEq for SharedPtr<T>
295where
296 T: PartialEq + SharedPtrTarget,
297{
298 fn eq(&self, other: &Self) -> bool {
299 self.as_ref() == other.as_ref()
300 }
301}
302
303impl<T> Eq for SharedPtr<T> where T: Eq + SharedPtrTarget {}
304
305impl<T> PartialOrd for SharedPtr<T>
306where
307 T: PartialOrd + SharedPtrTarget,
308{
309 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
310 PartialOrd::partial_cmp(&self.as_ref(), &other.as_ref())
311 }
312}
313
314impl<T> Ord for SharedPtr<T>
315where
316 T: Ord + SharedPtrTarget,
317{
318 fn cmp(&self, other: &Self) -> Ordering {
319 Ord::cmp(&self.as_ref(), &other.as_ref())
320 }
321}
322
323impl<T> Hash for SharedPtr<T>
324where
325 T: Hash + SharedPtrTarget,
326{
327 fn hash<H>(&self, hasher: &mut H)
328 where
329 H: Hasher,
330 {
331 self.as_ref().hash(hasher);
332 }
333}
334
335impl<T> From<UniquePtr<T>> for SharedPtr<T>
336where
337 T: UniquePtrTarget + SharedPtrTarget,
338{
339 fn from(unique: UniquePtr<T>) -> Self {
340 unsafe { SharedPtr::from_raw(UniquePtr::into_raw(unique)) }
341 }
342}
343
344/// Trait bound for types which may be used as the `T` inside of a
345/// `SharedPtr<T>` in generic code.
346///
347/// This trait has no publicly callable or implementable methods. Implementing
348/// it outside of the CXX codebase is not supported.
349///
350/// # Example
351///
352/// A bound `T: SharedPtrTarget` may be necessary when manipulating
353/// [`SharedPtr`] in generic code.
354///
355/// ```
356/// use cxx::memory::{SharedPtr, SharedPtrTarget};
357/// use std::fmt::Display;
358///
359/// pub fn take_generic_ptr<T>(ptr: SharedPtr<T>)
360/// where
361/// T: SharedPtrTarget + Display,
362/// {
363/// println!("the shared_ptr points to: {}", *ptr);
364/// }
365/// ```
366///
367/// Writing the same generic function without a `SharedPtrTarget` trait bound
368/// would not compile.
369pub unsafe trait SharedPtrTarget {
370 #[doc(hidden)]
371 fn __typename(f: &mut fmt::Formatter) -> fmt::Result;
372 #[doc(hidden)]
373 unsafe fn __null(new: *mut c_void);
374 #[doc(hidden)]
375 unsafe fn __new(value: Self, new: *mut c_void)
376 where
377 Self: Sized,
378 {
379 // Opaque C types do not get this method because they can never exist by
380 // value on the Rust side of the bridge.
381 let _ = value;
382 let _ = new;
383 unreachable!()
384 }
385 #[doc(hidden)]
386 unsafe fn __raw(new: *mut c_void, raw: *mut Self);
387 #[doc(hidden)]
388 unsafe fn __clone(this: *const c_void, new: *mut c_void);
389 #[doc(hidden)]
390 unsafe fn __get(this: *const c_void) -> *const Self;
391 #[doc(hidden)]
392 unsafe fn __drop(this: *mut c_void);
393}
394
395macro_rules! impl_shared_ptr_target {
396 ($segment:expr, $name:expr, $ty:ty) => {
397 unsafe impl SharedPtrTarget for $ty {
398 fn __typename(f: &mut fmt::Formatter) -> fmt::Result {
399 f.write_str($name)
400 }
401 unsafe fn __null(new: *mut c_void) {
402 extern "C" {
403 #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$null")]
404 fn __null(new: *mut c_void);
405 }
406 unsafe { __null(new) }
407 }
408 unsafe fn __new(value: Self, new: *mut c_void) {
409 extern "C" {
410 #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$uninit")]
411 fn __uninit(new: *mut c_void) -> *mut c_void;
412 }
413 unsafe { __uninit(new).cast::<$ty>().write(value) }
414 }
415 unsafe fn __raw(new: *mut c_void, raw: *mut Self) {
416 extern "C" {
417 #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$raw")]
418 fn __raw(new: *mut c_void, raw: *mut c_void);
419 }
420 unsafe { __raw(new, raw as *mut c_void) }
421 }
422 unsafe fn __clone(this: *const c_void, new: *mut c_void) {
423 extern "C" {
424 #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$clone")]
425 fn __clone(this: *const c_void, new: *mut c_void);
426 }
427 unsafe { __clone(this, new) }
428 }
429 unsafe fn __get(this: *const c_void) -> *const Self {
430 extern "C" {
431 #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$get")]
432 fn __get(this: *const c_void) -> *const c_void;
433 }
434 unsafe { __get(this) }.cast()
435 }
436 unsafe fn __drop(this: *mut c_void) {
437 extern "C" {
438 #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$drop")]
439 fn __drop(this: *mut c_void);
440 }
441 unsafe { __drop(this) }
442 }
443 }
444 };
445}
446
447macro_rules! impl_shared_ptr_target_for_primitive {
448 ($ty:ident) => {
449 impl_shared_ptr_target!(stringify!($ty), stringify!($ty), $ty);
450 };
451}
452
453impl_shared_ptr_target_for_primitive!(bool);
454impl_shared_ptr_target_for_primitive!(u8);
455impl_shared_ptr_target_for_primitive!(u16);
456impl_shared_ptr_target_for_primitive!(u32);
457impl_shared_ptr_target_for_primitive!(u64);
458impl_shared_ptr_target_for_primitive!(usize);
459impl_shared_ptr_target_for_primitive!(i8);
460impl_shared_ptr_target_for_primitive!(i16);
461impl_shared_ptr_target_for_primitive!(i32);
462impl_shared_ptr_target_for_primitive!(i64);
463impl_shared_ptr_target_for_primitive!(isize);
464impl_shared_ptr_target_for_primitive!(f32);
465impl_shared_ptr_target_for_primitive!(f64);
466
467impl_shared_ptr_target!("string", "CxxString", CxxString);