| David Tolnay | ac5af50 | 2021-03-25 00:29:06 -0400 | [diff] [blame] | 1 | use crate::fmt::display; |
| David Tolnay | b3b24a1 | 2020-12-01 15:27:43 -0800 | [diff] [blame] | 2 | use crate::kind::Trivial; |
| David Tolnay | bac2582 | 2020-12-12 23:13:51 -0800 | [diff] [blame] | 3 | use crate::string::CxxString; |
| David Tolnay | 85b6bc4 | 2020-12-28 17:47:51 -0800 | [diff] [blame] | 4 | use crate::weak_ptr::{WeakPtr, WeakPtrTarget}; |
| David Tolnay | b3b24a1 | 2020-12-01 15:27:43 -0800 | [diff] [blame] | 5 | use crate::ExternType; |
| 6 | use core::ffi::c_void; |
| 7 | use core::fmt::{self, Debug, Display}; |
| 8 | use core::marker::PhantomData; |
| 9 | use core::mem::MaybeUninit; |
| David Tolnay | 9df2aac | 2020-12-10 20:06:56 -0800 | [diff] [blame] | 10 | use core::ops::Deref; |
| David Tolnay | b3b24a1 | 2020-12-01 15:27:43 -0800 | [diff] [blame] | 11 | |
| David Tolnay | 40f4b56 | 2020-12-28 15:04:54 -0800 | [diff] [blame] | 12 | /// Binding to C++ `std::shared_ptr<T>`. |
| David Tolnay | b3b24a1 | 2020-12-01 15:27:43 -0800 | [diff] [blame] | 13 | #[repr(C)] |
| 14 | pub struct SharedPtr<T> |
| 15 | where |
| 16 | T: SharedPtrTarget, |
| 17 | { |
| David Tolnay | 46cff54 | 2021-05-02 10:59:57 -0700 | [diff] [blame^] | 18 | repr: [MaybeUninit<*mut c_void>; 2], |
| David Tolnay | b3b24a1 | 2020-12-01 15:27:43 -0800 | [diff] [blame] | 19 | ty: PhantomData<T>, |
| 20 | } |
| 21 | |
| 22 | impl<T> SharedPtr<T> |
| 23 | where |
| 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 | |
| Vitaly Shukela | cbad4f3 | 2021-01-15 09:25:41 +0300 | [diff] [blame] | 51 | /// Checks whether the SharedPtr does not own an object. |
| David Tolnay | b3b24a1 | 2020-12-01 15:27:43 -0800 | [diff] [blame] | 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 | } |
| David Tolnay | 85b6bc4 | 2020-12-28 17:47:51 -0800 | [diff] [blame] | 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 | } |
| David Tolnay | b3b24a1 | 2020-12-01 15:27:43 -0800 | [diff] [blame] | 84 | } |
| 85 | |
| 86 | unsafe impl<T> Send for SharedPtr<T> where T: Send + Sync + SharedPtrTarget {} |
| 87 | unsafe impl<T> Sync for SharedPtr<T> where T: Send + Sync + SharedPtrTarget {} |
| 88 | |
| 89 | impl<T> Clone for SharedPtr<T> |
| 90 | where |
| 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 | impl<T> Drop for SharedPtr<T> |
| 105 | where |
| 106 | T: SharedPtrTarget, |
| 107 | { |
| 108 | fn drop(&mut self) { |
| 109 | let this = self as *mut Self as *mut c_void; |
| 110 | unsafe { T::__drop(this) } |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | impl<T> Deref for SharedPtr<T> |
| 115 | where |
| 116 | T: SharedPtrTarget, |
| 117 | { |
| 118 | type Target = T; |
| 119 | |
| 120 | fn deref(&self) -> &Self::Target { |
| 121 | match self.as_ref() { |
| 122 | Some(target) => target, |
| David Tolnay | ac5af50 | 2021-03-25 00:29:06 -0400 | [diff] [blame] | 123 | None => panic!( |
| 124 | "called deref on a null SharedPtr<{}>", |
| 125 | display(T::__typename), |
| 126 | ), |
| David Tolnay | b3b24a1 | 2020-12-01 15:27:43 -0800 | [diff] [blame] | 127 | } |
| 128 | } |
| 129 | } |
| 130 | |
| David Tolnay | b3b24a1 | 2020-12-01 15:27:43 -0800 | [diff] [blame] | 131 | impl<T> Debug for SharedPtr<T> |
| 132 | where |
| 133 | T: Debug + SharedPtrTarget, |
| 134 | { |
| 135 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
| 136 | match self.as_ref() { |
| 137 | None => formatter.write_str("nullptr"), |
| 138 | Some(value) => Debug::fmt(value, formatter), |
| 139 | } |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | impl<T> Display for SharedPtr<T> |
| 144 | where |
| 145 | T: Display + SharedPtrTarget, |
| 146 | { |
| 147 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
| 148 | match self.as_ref() { |
| 149 | None => formatter.write_str("nullptr"), |
| 150 | Some(value) => Display::fmt(value, formatter), |
| 151 | } |
| 152 | } |
| 153 | } |
| 154 | |
| David Tolnay | 9291d52 | 2021-03-25 02:04:44 -0400 | [diff] [blame] | 155 | /// Trait bound for types which may be used as the `T` inside of a |
| 156 | /// `SharedPtr<T>` in generic code. |
| 157 | /// |
| 158 | /// This trait has no publicly callable or implementable methods. Implementing |
| 159 | /// it outside of the CXX codebase is not supported. |
| 160 | /// |
| 161 | /// # Example |
| 162 | /// |
| 163 | /// A bound `T: SharedPtrTarget` may be necessary when manipulating |
| 164 | /// [`SharedPtr`] in generic code. |
| 165 | /// |
| 166 | /// ``` |
| 167 | /// use cxx::memory::{SharedPtr, SharedPtrTarget}; |
| 168 | /// use std::fmt::Display; |
| 169 | /// |
| 170 | /// pub fn take_generic_ptr<T>(ptr: SharedPtr<T>) |
| 171 | /// where |
| 172 | /// T: SharedPtrTarget + Display, |
| 173 | /// { |
| 174 | /// println!("the shared_ptr points to: {}", *ptr); |
| 175 | /// } |
| 176 | /// ``` |
| 177 | /// |
| 178 | /// Writing the same generic function without a `SharedPtrTarget` trait bound |
| 179 | /// would not compile. |
| David Tolnay | b3b24a1 | 2020-12-01 15:27:43 -0800 | [diff] [blame] | 180 | pub unsafe trait SharedPtrTarget { |
| 181 | #[doc(hidden)] |
| David Tolnay | ac5af50 | 2021-03-25 00:29:06 -0400 | [diff] [blame] | 182 | fn __typename(f: &mut fmt::Formatter) -> fmt::Result; |
| David Tolnay | b3b24a1 | 2020-12-01 15:27:43 -0800 | [diff] [blame] | 183 | #[doc(hidden)] |
| 184 | unsafe fn __null(new: *mut c_void); |
| 185 | #[doc(hidden)] |
| 186 | unsafe fn __new(value: Self, new: *mut c_void) |
| 187 | where |
| 188 | Self: Sized, |
| 189 | { |
| 190 | // Opoaque C types do not get this method because they can never exist |
| 191 | // by value on the Rust side of the bridge. |
| 192 | let _ = value; |
| 193 | let _ = new; |
| 194 | unreachable!() |
| 195 | } |
| 196 | #[doc(hidden)] |
| 197 | unsafe fn __clone(this: *const c_void, new: *mut c_void); |
| 198 | #[doc(hidden)] |
| 199 | unsafe fn __get(this: *const c_void) -> *const Self; |
| 200 | #[doc(hidden)] |
| 201 | unsafe fn __drop(this: *mut c_void); |
| 202 | } |
| David Tolnay | 5b16340 | 2020-12-10 19:26:02 -0800 | [diff] [blame] | 203 | |
| 204 | macro_rules! impl_shared_ptr_target { |
| 205 | ($segment:expr, $name:expr, $ty:ty) => { |
| 206 | unsafe impl SharedPtrTarget for $ty { |
| David Tolnay | b99359b | 2021-03-25 02:05:20 -0400 | [diff] [blame] | 207 | #[doc(hidden)] |
| David Tolnay | ac5af50 | 2021-03-25 00:29:06 -0400 | [diff] [blame] | 208 | fn __typename(f: &mut fmt::Formatter) -> fmt::Result { |
| 209 | f.write_str($name) |
| 210 | } |
| David Tolnay | b99359b | 2021-03-25 02:05:20 -0400 | [diff] [blame] | 211 | #[doc(hidden)] |
| David Tolnay | 5b16340 | 2020-12-10 19:26:02 -0800 | [diff] [blame] | 212 | unsafe fn __null(new: *mut c_void) { |
| 213 | extern "C" { |
| 214 | attr! { |
| 215 | #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$null")] |
| 216 | fn __null(new: *mut c_void); |
| 217 | } |
| 218 | } |
| 219 | __null(new); |
| 220 | } |
| David Tolnay | b99359b | 2021-03-25 02:05:20 -0400 | [diff] [blame] | 221 | #[doc(hidden)] |
| David Tolnay | 5b16340 | 2020-12-10 19:26:02 -0800 | [diff] [blame] | 222 | unsafe fn __new(value: Self, new: *mut c_void) { |
| 223 | extern "C" { |
| 224 | attr! { |
| 225 | #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$uninit")] |
| 226 | fn __uninit(new: *mut c_void) -> *mut c_void; |
| 227 | } |
| 228 | } |
| 229 | __uninit(new).cast::<$ty>().write(value); |
| 230 | } |
| David Tolnay | b99359b | 2021-03-25 02:05:20 -0400 | [diff] [blame] | 231 | #[doc(hidden)] |
| David Tolnay | 5b16340 | 2020-12-10 19:26:02 -0800 | [diff] [blame] | 232 | unsafe fn __clone(this: *const c_void, new: *mut c_void) { |
| 233 | extern "C" { |
| 234 | attr! { |
| 235 | #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$clone")] |
| 236 | fn __clone(this: *const c_void, new: *mut c_void); |
| 237 | } |
| 238 | } |
| 239 | __clone(this, new); |
| 240 | } |
| David Tolnay | b99359b | 2021-03-25 02:05:20 -0400 | [diff] [blame] | 241 | #[doc(hidden)] |
| David Tolnay | 5b16340 | 2020-12-10 19:26:02 -0800 | [diff] [blame] | 242 | unsafe fn __get(this: *const c_void) -> *const Self { |
| 243 | extern "C" { |
| 244 | attr! { |
| 245 | #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$get")] |
| 246 | fn __get(this: *const c_void) -> *const c_void; |
| 247 | } |
| 248 | } |
| 249 | __get(this).cast() |
| 250 | } |
| David Tolnay | b99359b | 2021-03-25 02:05:20 -0400 | [diff] [blame] | 251 | #[doc(hidden)] |
| David Tolnay | 5b16340 | 2020-12-10 19:26:02 -0800 | [diff] [blame] | 252 | unsafe fn __drop(this: *mut c_void) { |
| 253 | extern "C" { |
| 254 | attr! { |
| 255 | #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$drop")] |
| 256 | fn __drop(this: *mut c_void); |
| 257 | } |
| 258 | } |
| 259 | __drop(this); |
| 260 | } |
| 261 | } |
| 262 | }; |
| 263 | } |
| 264 | |
| 265 | macro_rules! impl_shared_ptr_target_for_primitive { |
| 266 | ($ty:ident) => { |
| 267 | impl_shared_ptr_target!(stringify!($ty), stringify!($ty), $ty); |
| 268 | }; |
| 269 | } |
| 270 | |
| David Tolnay | cd1430c | 2020-12-28 17:17:14 -0800 | [diff] [blame] | 271 | impl_shared_ptr_target_for_primitive!(bool); |
| David Tolnay | 5b16340 | 2020-12-10 19:26:02 -0800 | [diff] [blame] | 272 | impl_shared_ptr_target_for_primitive!(u8); |
| 273 | impl_shared_ptr_target_for_primitive!(u16); |
| 274 | impl_shared_ptr_target_for_primitive!(u32); |
| 275 | impl_shared_ptr_target_for_primitive!(u64); |
| 276 | impl_shared_ptr_target_for_primitive!(usize); |
| 277 | impl_shared_ptr_target_for_primitive!(i8); |
| 278 | impl_shared_ptr_target_for_primitive!(i16); |
| 279 | impl_shared_ptr_target_for_primitive!(i32); |
| 280 | impl_shared_ptr_target_for_primitive!(i64); |
| 281 | impl_shared_ptr_target_for_primitive!(isize); |
| 282 | impl_shared_ptr_target_for_primitive!(f32); |
| 283 | impl_shared_ptr_target_for_primitive!(f64); |
| 284 | |
| 285 | impl_shared_ptr_target!("string", "CxxString", CxxString); |