cpp_core/
cpp_box.rs

1use crate::ops::{Begin, BeginMut, End, EndMut, Increment, Indirection};
2use crate::vector_ops::{Data, DataMut, Size};
3use crate::{cpp_iter, CppIterator, DynamicCast, Ptr, Ref, StaticDowncast, StaticUpcast};
4use std::ops::Deref;
5use std::{fmt, mem, ptr, slice};
6
7/// Objects that can be deleted using C++'s `delete` operator.
8///
9/// This trait is automatically implemented for class types by `ritual`.
10pub trait CppDeletable: Sized {
11    /// Calls C++'s `delete x` on `self`.
12    ///
13    /// # Safety
14    ///
15    /// The caller must make sure `self` contains a valid pointer. This function
16    /// may invoke arbitrary foreign code, so no safety guarantees can be made.
17    /// Note that deleting an object multiple times is undefined behavior.
18    unsafe fn delete(&self);
19}
20
21/// An owning pointer to a C++ object.
22///
23/// `CppBox` is automatically used in places where C++ class objects are passed by value
24/// and in return values of constructors because the ownership is apparent in these cases.
25/// However, sometimes an object is returned as a pointer but you must accept the ownership
26/// of the object. It's not possible to automatically determine ownership semantics
27/// of C++ code in this case, so you should manually convert `Ptr` to `CppBox`
28/// using `to_box` method.
29///
30/// When `CppBox` is dropped, it will automatically delete the object using C++'s `delete`
31/// operator.
32///
33/// Objects stored in `CppBox` are usually placed on the heap by the C++ code.
34///
35/// If a C++ API accepts an object by pointer and takes ownership of it, it's not possible to
36/// automatically detect this, so you must manually convert `CppBox` to a non-owning `Ptr`
37/// using `into_ptr` before passing it to such a function.
38///
39/// `&CppBox<T>` and `&mut CppBox<T>` implement operator traits and delegate them
40/// to the corresponding C++ operators.
41/// This means that you can use `&box1 + value` to access the object's `operator+`.
42///
43/// `CppBox` implements `Deref`, allowing to call the object's methods
44/// directly. In addition, methods of the object's first base class are also directly available
45/// thanks to nested `Deref` implementations.
46///
47/// If the object provides an iterator interface through `begin()` and `end()` functions,
48/// `&CppBox<T>` and `&mut CppBox<T>` will implement `IntoIterator`,
49/// so you can iterate on them directly.
50///
51/// ### Safety
52///
53/// It's not possible to automatically track the ownership of objects possibly managed by C++
54/// libraries. The user must ensure that the object is alive while `CppBox` exists and that
55/// no pointers to the object are used after the object is deleted
56/// by `CppBox`'s `Drop` implementation. Note that with `CppBox`,
57/// it's possible to call unsafe C++ code without using any more unsafe code, for example, by
58/// using operator traits or simply dropping the box, so care should be taken when exposing
59/// `CppBox` in a safe interface.
60pub struct CppBox<T: CppDeletable>(ptr::NonNull<T>);
61
62impl<T: CppDeletable> CppBox<T> {
63    /// Encapsulates the object into a `CppBox`. Returns `None` if the pointer is null.
64    ///
65    /// The same operation can be done by calling `to_box` function on `Ptr`.
66    ///
67    /// You should use this function only for
68    /// pointers that were created on C++ side and passed through
69    /// a FFI boundary to Rust. An object created with C++ `new`
70    /// must be deleted using C++ `delete`, which is executed by `CppBox`.
71    ///
72    /// Do not use this function for objects that would be deleted by other means.
73    /// If another C++ object is the owner of the passed object,
74    /// it will attempt to delete it. If `CppBox` containing the object still exists,
75    /// it would result in a double deletion, which must never happen.
76    ///
77    /// Use `CppBox::into_ptr` to unwrap the pointer before passing it to
78    /// a function that takes ownership of the object.
79    ///
80    /// ### Safety
81    ///
82    /// The pointer must point to an object that can be
83    /// safely deleted using C++'s `delete` operator.
84    /// The object must not be deleted by other means while `CppBox` exists.
85    /// Any other pointers to the object must not be used after `CppBox` is dropped.
86    pub unsafe fn new(ptr: Ptr<T>) -> Option<Self> {
87        Self::from_raw(ptr.as_raw_ptr())
88    }
89
90    /// Encapsulates the object into a `CppBox`. Returns `None` if the pointer is null.
91    ///
92    /// See `CppBox::new` for more information.
93    ///
94    /// ### Safety
95    ///
96    /// The pointer must point to an object that can be
97    /// safely deleted using C++'s `delete` operator.
98    /// The object must not be deleted by other means while `CppBox` exists.
99    /// Any other pointers to the object must not be used after `CppBox` is dropped.
100    pub unsafe fn from_raw(ptr: *const T) -> Option<Self> {
101        ptr::NonNull::new(ptr as *mut T).map(CppBox)
102    }
103
104    /// Returns a constant pointer to the value in the box.
105    ///
106    /// ### Safety
107    ///
108    /// This operation is safe as long as `self` is valid.
109    pub unsafe fn as_ptr(&self) -> Ptr<T> {
110        Ptr::from_raw(self.0.as_ptr())
111    }
112
113    /// Returns a constant raw pointer to the value in the box.
114    pub fn as_mut_raw_ptr(&self) -> *mut T {
115        self.0.as_ptr()
116    }
117
118    /// Returns a mutable raw pointer to the value in the box.
119    pub fn as_raw_ptr(&self) -> *const T {
120        self.0.as_ptr() as *const T
121    }
122
123    /// Destroys the box without deleting the object and returns a raw pointer to the content.
124    /// The caller of the function becomes the owner of the object and should
125    /// ensure that the object will be deleted at some point.
126    pub fn into_raw_ptr(self) -> *mut T {
127        let ptr = self.0.as_ptr();
128        mem::forget(self);
129        ptr
130    }
131
132    /// Destroys the box without deleting the object and returns a pointer to the content.
133    /// The caller of the function becomes the owner of the object and should
134    /// ensure that the object will be deleted at some point.
135    ///
136    /// ### Safety
137    ///
138    /// This operation is safe as long as `self` is valid.
139    pub unsafe fn into_ptr(self) -> Ptr<T> {
140        let ptr = Ptr::from_raw(self.0.as_ptr());
141        mem::forget(self);
142        ptr
143    }
144
145    /// Returns a constant reference to the value in the box.
146    ///
147    /// ### Safety
148    ///
149    /// This operation is safe as long as `self` is valid.
150    #[allow(clippy::should_implement_trait)]
151    pub unsafe fn as_ref(&self) -> Ref<T> {
152        Ref::from_raw_non_null(self.0)
153    }
154
155    /// Returns a reference to the value.
156    ///
157    /// ### Safety
158    ///
159    /// `self` must be valid.
160    /// The content must not be read or modified through other ways while the returned reference
161    /// exists.See type level documentation.
162    pub unsafe fn as_raw_ref<'a>(&self) -> &'a T {
163        &*self.0.as_ptr()
164    }
165
166    /// Returns a mutable reference to the value.
167    ///
168    /// ### Safety
169    ///
170    /// `self` must be valid.
171    /// The content must not be read or modified through other ways while the returned reference
172    /// exists.See type level documentation.
173    pub unsafe fn as_mut_raw_ref<'a>(&self) -> &'a mut T {
174        &mut *self.0.as_ptr()
175    }
176
177    /// Returns a non-owning reference to the content converted to the base class type `U`.
178    /// `CppBox` retains the ownership of the object.
179    ///
180    /// ### Safety
181    ///
182    /// This operation is safe as long as `self` is valid.
183    pub unsafe fn static_upcast<U>(&self) -> Ref<U>
184    where
185        T: StaticUpcast<U>,
186    {
187        StaticUpcast::static_upcast(self.as_ptr())
188            .as_ref()
189            .expect("StaticUpcast returned null on CppBox input")
190    }
191
192    /// Returns a non-owning reference to the content converted to the derived class type `U`.
193    /// `CppBox` retains the ownership of the object.
194    ///
195    /// It's recommended to use `dynamic_cast` instead because it performs a checked conversion.
196    ///
197    /// ### Safety
198    ///
199    /// This operation is safe as long as `self` is valid and it's type is `U` or inherits from `U`.
200    pub unsafe fn static_downcast<U>(&self) -> Ref<U>
201    where
202        T: StaticDowncast<U>,
203    {
204        StaticDowncast::static_downcast(self.as_ptr())
205            .as_ref()
206            .expect("StaticDowncast returned null on CppBox input")
207    }
208
209    /// Returns a non-owning reference to the content converted to the derived class type `U`.
210    /// `CppBox` retains the ownership of the object. Returns `None` if the object's type is not `U`
211    /// and doesn't inherit `U`.
212    ///
213    /// ### Safety
214    ///
215    /// This operation is safe as long as `self` is valid.
216    pub unsafe fn dynamic_cast<U>(&self) -> Option<Ref<U>>
217    where
218        T: DynamicCast<U>,
219    {
220        DynamicCast::dynamic_cast(self.as_ptr()).as_ref()
221    }
222}
223
224impl<V, T> CppBox<V>
225where
226    V: Data<Output = *const T> + Size + CppDeletable,
227{
228    /// Returns the content of the object as a slice, based on `data()` and `size()` methods.
229    ///
230    /// # Safety
231    ///
232    /// The caller must make sure `self` contains a valid pointer. The content must
233    /// not be read or modified through other ways while the returned slice exists.
234    /// This function
235    /// may invoke arbitrary foreign code, so no safety guarantees can be made.
236    pub unsafe fn as_slice<'a>(&self) -> &'a [T] {
237        let ptr = self.data();
238        let size = self.size();
239        slice::from_raw_parts(ptr, size)
240    }
241}
242
243impl<V, T> CppBox<V>
244where
245    V: DataMut<Output = *mut T> + Size + CppDeletable,
246{
247    /// Returns the content of the vector as a mutable slice,
248    /// based on `data()` and `size()` methods.
249    ///
250    /// # Safety
251    ///
252    /// The caller must make sure `self` contains a valid pointer. The content must
253    /// not be read or modified through other ways while the returned slice exists.
254    /// This function
255    /// may invoke arbitrary foreign code, so no safety guarantees can be made.
256    pub unsafe fn as_mut_slice<'a>(&self) -> &'a mut [T] {
257        let ptr = self.data_mut();
258        let size = self.size();
259        slice::from_raw_parts_mut(ptr, size)
260    }
261}
262
263impl<T, T1, T2> CppBox<T>
264where
265    T: Begin<Output = CppBox<T1>> + End<Output = CppBox<T2>> + CppDeletable,
266    T1: CppDeletable + PartialEq<Ref<T2>> + Increment + Indirection,
267    T2: CppDeletable,
268{
269    /// Returns an iterator over the content of the object,
270    /// based on `begin()` and `end()` methods.
271    ///
272    /// # Safety
273    ///
274    /// The caller must make sure `self` contains a valid pointer. The content must
275    /// not be read or modified through other ways while the returned slice exists.
276    /// This function
277    /// may invoke arbitrary foreign code, so no safety guarantees can be made.
278    pub unsafe fn iter(&self) -> CppIterator<T1, T2> {
279        cpp_iter(self.begin(), self.end())
280    }
281}
282
283impl<T, T1, T2> CppBox<T>
284where
285    T: BeginMut<Output = CppBox<T1>> + EndMut<Output = CppBox<T2>> + CppDeletable,
286    T1: CppDeletable + PartialEq<Ref<T2>> + Increment + Indirection,
287    T2: CppDeletable,
288{
289    /// Returns a mutable iterator over the content of the object,
290    /// based on `begin()` and `end()` methods.
291    ///
292    /// # Safety
293    ///
294    /// The caller must make sure `self` contains a valid pointer. The content must
295    /// not be read or modified through other ways while the returned slice exists.
296    /// This function
297    /// may invoke arbitrary foreign code, so no safety guarantees can be made.
298    pub unsafe fn iter_mut(&self) -> CppIterator<T1, T2> {
299        cpp_iter(self.begin_mut(), self.end_mut())
300    }
301}
302
303/// Allows to call member functions of `T` and its base classes directly on the pointer.
304impl<T: CppDeletable> Deref for CppBox<T> {
305    type Target = T;
306    fn deref(&self) -> &T {
307        unsafe { self.0.as_ref() }
308    }
309}
310
311/// Deletes the stored object using C++'s `delete` operator.
312impl<T: CppDeletable> Drop for CppBox<T> {
313    fn drop(&mut self) {
314        unsafe {
315            T::delete(self.0.as_ref());
316        }
317    }
318}
319
320impl<T: CppDeletable> fmt::Debug for CppBox<T> {
321    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
322        write!(f, "CppBox({:?})", self.0)
323    }
324}
325
326#[cfg(test)]
327mod tests {
328    use crate::{CppBox, CppDeletable, Ptr};
329    use std::cell::RefCell;
330    use std::rc::Rc;
331
332    struct Struct1 {
333        value: Rc<RefCell<i32>>,
334    }
335
336    unsafe extern "C" fn struct1_delete(this_ptr: *const Struct1) {
337        (*this_ptr).value.borrow_mut().clone_from(&42);
338    }
339
340    impl CppDeletable for Struct1 {
341        unsafe fn delete(&self) {
342            struct1_delete(self);
343        }
344    }
345
346    #[test]
347    fn test_drop_calls_deleter() {
348        let value1 = Rc::new(RefCell::new(10));
349        let object1 = Struct1 {
350            value: value1.clone(),
351        };
352        assert!(*value1.borrow() == 10);
353        unsafe {
354            CppBox::new(Ptr::from_raw(&object1));
355        }
356        assert!(*value1.borrow() == 42);
357    }
358}