1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
#[cfg(test)] mod tests { use std::rc::Rc; use std::cell::RefCell; use {CppDeletable, Deleter, CppBox}; struct Struct1 { value: Rc<RefCell<i32>>, } unsafe extern "C" fn struct1_delete(this_ptr: *mut Struct1) { (*this_ptr).value.borrow_mut().clone_from(&mut 42); } impl CppDeletable for Struct1 { fn deleter() -> Deleter<Self> { struct1_delete } } #[test] fn test_drop_calls_deleter() { let value1 = Rc::new(RefCell::new(10)); let mut object1 = Struct1 { value: value1.clone() }; assert!(value1.borrow().clone() == 10); unsafe { CppBox::new(&mut object1 as *mut _); } assert!(value1.borrow().clone() == 42); } } /// Deleter function type. /// /// This is usually a C++ function imported via FFI /// from a wrapper library. The body of this function /// should be "delete this_ptr;". pub type Deleter<T> = unsafe extern "C" fn(this_ptr: *mut T); /// Indicates that the type can be put into a CppBox. /// /// Example of implementation: /// ``` /// impl CppDeletable for Struct1 { /// fn deleter() -> Deleter<Self> { /// struct1_delete /// } /// } /// ``` pub trait CppDeletable : Sized { /// Returns deleter function for this type. fn deleter() -> Deleter<Self>; } /// A C++ pointer wrapper to manage deletion of objects. /// /// Objects of CppBox should be created by calling into_box() for /// types that implement CppDeletable trait. The object will /// be deleted when corresponding CppBox is deleted. pub struct CppBox<T> { ptr: *mut T, deleter: Deleter<T>, } impl<T> CppBox<T> { pub fn as_ptr(&self) -> *const T { self.ptr } pub fn as_mut_ptr(&self) -> *mut T { self.ptr } /// Returns the pointer that was used to create the object and destroys the box. /// The caller of the function becomes the owner of the object and should /// ensure that the object will be deleted at some point. pub fn into_raw(mut self) -> *mut T { let ptr = self.ptr; self.ptr = std::ptr::null_mut(); ptr } } impl<T: CppDeletable> CppBox<T> { /// Encapsulates the object into a CppBox. /// /// You should use this function only for /// pointers that were created on C++ side and passed through /// a FFI boundary to Rust. An object created with C++ "new" /// must be deleted using C++ "delete", which is executed by CppBox. /// /// Do not use this function for objects created in memory managed by Rust. /// Any wrapper constructor or function that returns an owned object /// is supposed to be deleted using Rust's ownage system and Drop trait. /// /// Do not use this function for objects that would be deleted by other means. /// If another C++ object is the owner of the passed object, /// it will attempt to delete it. Together with CppBox, it would result /// in a double deletion, which should never happen. pub unsafe fn new(ptr: *mut T) -> CppBox<T> { CppBox { ptr: ptr, deleter: CppDeletable::deleter() } } } impl<T> AsRef<T> for CppBox<T> { fn as_ref(&self) -> &T { unsafe { &*self.ptr } } } impl<T> AsMut<T> for CppBox<T> { fn as_mut(&mut self) -> &mut T { unsafe { &mut *self.ptr } } } impl<T> std::ops::Deref for CppBox<T> { type Target = T; fn deref(&self) -> &T { unsafe { &*self.ptr } } } impl<T> std::ops::DerefMut for CppBox<T> { fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.ptr } } } impl<T> Drop for CppBox<T> { fn drop(&mut self) { if !self.ptr.is_null() { unsafe { (self.deleter)(self.ptr); } } } } /// Additional argument for a function that returns a C++ object as Rust struct. pub struct AsStruct; /// Additional argument for a function that returns a C++ object as a pointer /// enclosed in a CppBox. pub struct AsBox; /// This module contains `NewUninitialized` trait which should not be used directly. pub mod new_uninitialized { /// A trait for types that can be created with /// uninitialized internal buffer. /// /// This trait should not be used directly. /// It's only useful for the code automatically generated /// by cpp_to_rust. pub trait NewUninitialized { /// Creates new object with /// uninitialized internal buffer. unsafe fn new_uninitialized() -> Self; } } /// Provides access to C++ `static_cast` conversion from derived class to base class. /// /// This trait is automatically implemented by `cpp_to_rust`. /// If `T1` class is derived (in C++) from `T2` class, /// `StaticCast<T2>` is implemented for `T1`. /// /// `StaticCast` allows to convert a reference to a class into /// a reference to a base class. /// /// `static_cast` and `static_cast_mut` free functions can be used /// to convert pointer types. /// /// Note that Rust functions associated with this trait have runtime overhead. /// In C++, `static_cast` is usually a no-op if there is no multiple inheritance, /// and multiple inheritance requires pointer adjustment. However, Rust compiler /// and `cpp_to_rust` do not have any information about these implementation details, /// so all calls of `static_cast` are wrapper in FFI functions. /// Still, `static_cast` is faster than casts with runtime checks on C++ side /// because runtime overhead of Rust wrapper functions is the same for all cast types. pub trait StaticCast<T> { /// Convert type of a const reference. fn static_cast(&self) -> &T; /// Convert type of a mutable reference. fn static_cast_mut(&mut self) -> &mut T; } /// Converts type of a const pointer using `StaticCast` implementation of the type. /// If `ptr` is null, this function does nothing and returns null pointer. pub fn static_cast<R, T: StaticCast<R>>(ptr: *const T) -> *const R { unsafe { ptr.as_ref() }.map(|x| x.static_cast() as *const R).unwrap_or(std::ptr::null()) } /// Converts type of a mutable pointer using `StaticCast` implementation of the type. /// If `ptr` is null, this function does nothing and returns null pointer. pub fn static_cast_mut<R, T: StaticCast<R>>(ptr: *mut T) -> *mut R { unsafe { ptr.as_mut() }.map(|x| x.static_cast_mut() as *mut R).unwrap_or(std::ptr::null_mut()) } /// Provides access to C++ `static_cast` conversion from base class to derived class. /// /// This trait is automatically implemented by `cpp_to_rust`. /// If `T1` class is derived (in C++) from `T2` class, /// `UnsafeStaticCast<T1>` is implemented for `T2`. /// /// `UnsafeStaticCast` allows to convert a reference to a class into /// a reference to a derived class without runtime check of the type. /// Casting from base class type to a derived class type which is /// not the actual type of the object will result in an invalid reference. /// /// `unsafe_static_cast` and `unsafe_static_cast_mut` free functions can be used /// to convert pointer types. /// /// Note that Rust functions associated with this trait have runtime overhead. /// In C++, `static_cast` is usually a no-op if there is no multiple inheritance, /// and multiple inheritance requires pointer adjustment. However, Rust compiler /// and `cpp_to_rust` do not have any information about these implementation details, /// so all calls of `static_cast` are wrapper in FFI functions. /// Still, `static_cast` is faster than casts with runtime checks on C++ side /// because runtime overhead of Rust wrapper functions is the same for all cast types. pub trait UnsafeStaticCast<T> { /// Convert type of a const reference. unsafe fn static_cast(&self) -> &T; /// Convert type of a mutable reference. unsafe fn static_cast_mut(&mut self) -> &mut T; } /// Converts type of a const pointer using `UnsafeStaticCast` implementation of the type. /// `ptr` must be either a null pointer or a valid pointer to an instance of `R` class /// or a class derived from `R`. /// If `ptr` is null, this function does nothing and returns null pointer. pub unsafe fn unsafe_static_cast<R, T: UnsafeStaticCast<R>>(ptr: *const T) -> *const R { ptr.as_ref().map(|x| x.static_cast() as *const R).unwrap_or(std::ptr::null()) } /// Converts type of a mutable pointer using `UnsafeStaticCast` implementation of the type. /// `ptr` must be either a null pointer or a valid pointer to an instance of `R` class /// or a class derived from `R`. /// If `ptr` is null, this function does nothing and returns null pointer. pub unsafe fn unsafe_static_cast_mut<R, T: UnsafeStaticCast<R>>(ptr: *mut T) -> *mut R { ptr.as_mut().map(|x| x.static_cast_mut() as *mut R).unwrap_or(std::ptr::null_mut()) } /// Provides access to C++ `dynamic_cast` conversion. /// /// This trait is automatically implemented by `cpp_to_rust`. /// If `T1` class is derived (in C++) from `T2` class, /// `DynamicCast<T1>` is implemented for `T2`. /// Use `StaticCast` to convert from `T1` to `T2`. /// /// `DynamicCast` allows to convert a reference to a class into /// a reference to a derived class with a runtime check of the type. /// Conversion returns `None` if the object is actually not an instance of /// the target type. /// /// `dynamic_cast` and `dynamic_cast_mut` free functions can be used /// to convert pointer types. pub trait DynamicCast<T> { /// Convert type of a const reference. /// Returns `None` if `self` is not an instance of `T`. fn dynamic_cast(&self) -> Option<&T>; /// Convert type of a mutable reference. /// Returns `None` if `self` is not an instance of `T`. fn dynamic_cast_mut(&mut self) -> Option<&mut T>; } /// Converts type of a const pointer using `DynamicCast` implementation of the type. /// `ptr` must be either a null pointer or a valid pointer to an instance of `T` class /// or a class derived from `T`. /// Returns null pointer if `ptr` does not point to an instance of `R` or an instance of /// a class derived from `R`. /// If `ptr` is null, this function does nothing and returns null pointer. pub unsafe fn dynamic_cast<R, T: DynamicCast<R>>(ptr: *const T) -> *const R { ptr.as_ref().and_then(|x| x.dynamic_cast()).map(|x| x as *const R).unwrap_or(std::ptr::null()) } /// Converts type of a mutable pointer using `DynamicCast` implementation of the type. /// `ptr` must be either a null pointer or a valid pointer to an instance of `T` class /// or a class derived from `T`. /// Returns null pointer if `ptr` does not point to an instance of `R`. /// If `ptr` is null, this function does nothing and returns null pointer. pub unsafe fn dynamic_cast_mut<R, T: DynamicCast<R>>(ptr: *mut T) -> *mut R { ptr.as_mut().and_then(|x| x.dynamic_cast_mut()).map(|x| x as *mut R).unwrap_or(std::ptr::null_mut()) }