From 4fc21d970bdf8077476796638a7be948e7071444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Sun, 14 Jan 2018 13:21:50 +0100 Subject: [PATCH 1/5] Add macro that declare CF types with Drop impl --- core-foundation/src/base.rs | 26 ++++------------- core-foundation/src/boolean.rs | 16 ++++------- core-foundation/src/bundle.rs | 13 +++------ core-foundation/src/data.rs | 12 ++------ core-foundation/src/date.rs | 12 ++------ core-foundation/src/dictionary.rs | 12 ++------ core-foundation/src/error.rs | 12 ++------ core-foundation/src/lib.rs | 17 ++++++++++++ core-foundation/src/number.rs | 14 +++------- core-foundation/src/propertylist.rs | 41 +++++++++++++-------------- core-foundation/src/runloop.rs | 43 +++-------------------------- core-foundation/src/set.rs | 12 ++------ core-foundation/src/string.rs | 12 ++------ core-foundation/src/timezone.rs | 12 ++------ core-foundation/src/url.rs | 10 +------ core-foundation/src/uuid.rs | 12 ++------ 16 files changed, 83 insertions(+), 193 deletions(-) diff --git a/core-foundation/src/base.rs b/core-foundation/src/base.rs index 3d945e9..eb05e34 100644 --- a/core-foundation/src/base.rs +++ b/core-foundation/src/base.rs @@ -30,8 +30,10 @@ impl CFIndexConvertible for usize { } } -/// Superclass of all Core Foundation objects. -pub struct CFType(CFTypeRef); +declare_TCFType!{ + /// Superclass of all Core Foundation objects. + CFType, CFTypeRef +} impl fmt::Debug for CFType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -60,25 +62,7 @@ impl PartialEq for CFType { } } -impl Drop for CFType { - fn drop(&mut self) { - unsafe { - CFRelease(self.0) - } - } -} - -/// An allocator for Core Foundation objects. -pub struct CFAllocator(CFAllocatorRef); - -impl Drop for CFAllocator { - fn drop(&mut self) { - unsafe { - CFRelease(self.as_CFTypeRef()) - } - } -} - +declare_TCFType!(CFAllocator, CFAllocatorRef); impl_TCFType!(CFAllocator, CFAllocatorRef, CFAllocatorGetTypeID); impl CFAllocator { diff --git a/core-foundation/src/boolean.rs b/core-foundation/src/boolean.rs index e843467..14a9996 100644 --- a/core-foundation/src/boolean.rs +++ b/core-foundation/src/boolean.rs @@ -14,19 +14,13 @@ pub use core_foundation_sys::number::{CFBooleanRef, CFBooleanGetTypeID, kCFBoole use base::TCFType; -/// A Boolean type. -/// -/// FIXME(pcwalton): Should be a newtype struct, but that fails due to a Rust compiler bug. -pub struct CFBoolean(CFBooleanRef); -impl Drop for CFBoolean { - fn drop(&mut self) { - unsafe { - CFRelease(self.as_CFTypeRef()) - } - } +declare_TCFType!{ + /// A Boolean type. + /// + /// FIXME(pcwalton): Should be a newtype struct, but that fails due to a Rust compiler bug. + CFBoolean, CFBooleanRef } - impl_TCFType!(CFBoolean, CFBooleanRef, CFBooleanGetTypeID); impl_CFTypeDescription!(CFBoolean); diff --git a/core-foundation/src/bundle.rs b/core-foundation/src/bundle.rs index 4d75c30..4b50623 100644 --- a/core-foundation/src/bundle.rs +++ b/core-foundation/src/bundle.rs @@ -16,16 +16,12 @@ use base::TCFType; use url::CFURL; use dictionary::CFDictionary; -/// A Bundle type. -pub struct CFBundle(CFBundleRef); -impl Drop for CFBundle { - fn drop(&mut self) { - unsafe { - CFRelease(self.as_CFTypeRef()) - } - } +declare_TCFType!{ + /// A Bundle type. + CFBundle, CFBundleRef } +impl_TCFType!(CFBundle, CFBundleRef, CFBundleGetTypeID); impl CFBundle { pub fn new(bundleURL: CFURL) -> Option { @@ -76,7 +72,6 @@ impl CFBundle { } } -impl_TCFType!(CFBundle, CFBundleRef, CFBundleGetTypeID); #[test] fn safari_executable_url() { diff --git a/core-foundation/src/data.rs b/core-foundation/src/data.rs index baa95f2..6615b9a 100644 --- a/core-foundation/src/data.rs +++ b/core-foundation/src/data.rs @@ -17,17 +17,11 @@ use std::slice; use base::{CFIndexConvertible, TCFType}; -/// A byte buffer. -pub struct CFData(CFDataRef); -impl Drop for CFData { - fn drop(&mut self) { - unsafe { - CFRelease(self.as_CFTypeRef()) - } - } +declare_TCFType!{ + /// A byte buffer. + CFData, CFDataRef } - impl_TCFType!(CFData, CFDataRef, CFDataGetTypeID); impl_CFTypeDescription!(CFData); diff --git a/core-foundation/src/date.rs b/core-foundation/src/date.rs index ee78cf8..17a49b5 100644 --- a/core-foundation/src/date.rs +++ b/core-foundation/src/date.rs @@ -17,17 +17,11 @@ use base::TCFType; #[cfg(feature = "with-chrono")] use chrono::NaiveDateTime; -/// A date. -pub struct CFDate(CFDateRef); -impl Drop for CFDate { - fn drop(&mut self) { - unsafe { - CFRelease(self.as_CFTypeRef()) - } - } +declare_TCFType!{ + /// A date. + CFDate, CFDateRef } - impl_TCFType!(CFDate, CFDateRef, CFDateGetTypeID); impl_CFTypeDescription!(CFDate); impl_CFComparison!(CFDate, CFDateCompare); diff --git a/core-foundation/src/dictionary.rs b/core-foundation/src/dictionary.rs index 5d4a12b..c4c2f7c 100644 --- a/core-foundation/src/dictionary.rs +++ b/core-foundation/src/dictionary.rs @@ -18,17 +18,11 @@ use std::ptr; use base::{CFType, CFIndexConvertible, TCFType}; -/// An immutable dictionary of key-value pairs. -pub struct CFDictionary(CFDictionaryRef); -impl Drop for CFDictionary { - fn drop(&mut self) { - unsafe { - CFRelease(self.as_CFTypeRef()) - } - } +declare_TCFType!{ + /// An immutable dictionary of key-value pairs. + CFDictionary, CFDictionaryRef } - impl_TCFType!(CFDictionary, CFDictionaryRef, CFDictionaryGetTypeID); impl_CFTypeDescription!(CFDictionary); diff --git a/core-foundation/src/error.rs b/core-foundation/src/error.rs index af9b26f..39842e6 100644 --- a/core-foundation/src/error.rs +++ b/core-foundation/src/error.rs @@ -17,17 +17,11 @@ use std::fmt; use base::{CFIndex, TCFType}; use string::CFString; -/// An error value. -pub struct CFError(CFErrorRef); -impl Drop for CFError { - fn drop(&mut self) { - unsafe { - CFRelease(self.as_CFTypeRef()) - } - } +declare_TCFType!{ + /// An error value. + CFError, CFErrorRef } - impl_TCFType!(CFError, CFErrorRef, CFErrorGetTypeID); impl fmt::Debug for CFError { diff --git a/core-foundation/src/lib.rs b/core-foundation/src/lib.rs index f1f8b4f..30de654 100644 --- a/core-foundation/src/lib.rs +++ b/core-foundation/src/lib.rs @@ -14,6 +14,23 @@ extern crate libc; #[cfg(feature = "with-chrono")] extern crate chrono; +#[macro_export] +macro_rules! declare_TCFType { + ( + $(#[$doc:meta])* + $ty:ident, $raw:ident + ) => { + $(#[$doc])* + pub struct $ty($raw); + + impl Drop for $ty { + fn drop(&mut self) { + unsafe { CFRelease(self.as_CFTypeRef()) } + } + } + } +} + #[macro_export] macro_rules! impl_TCFType { ($ty:ident, $raw:ident, $ty_id:ident) => { diff --git a/core-foundation/src/number.rs b/core-foundation/src/number.rs index cd02635..d7b6992 100644 --- a/core-foundation/src/number.rs +++ b/core-foundation/src/number.rs @@ -13,19 +13,13 @@ use core_foundation_sys::base::{CFRelease, kCFAllocatorDefault}; pub use core_foundation_sys::number::*; use std::mem; -use base::{TCFType}; +use base::TCFType; -/// An immutable numeric value. -pub struct CFNumber(CFNumberRef); -impl Drop for CFNumber { - fn drop(&mut self) { - unsafe { - CFRelease(self.as_CFTypeRef()) - } - } +declare_TCFType!{ + /// An immutable numeric value. + CFNumber, CFNumberRef } - impl_TCFType!(CFNumber, CFNumberRef, CFNumberGetTypeID); impl_CFTypeDescription!(CFNumber); impl_CFComparison!(CFNumber, CFNumberCompare); diff --git a/core-foundation/src/propertylist.rs b/core-foundation/src/propertylist.rs index b8d5e02..ff89e0f 100644 --- a/core-foundation/src/propertylist.rs +++ b/core-foundation/src/propertylist.rs @@ -79,28 +79,25 @@ impl CFPropertyListSubClass<::date::__CFDate> for ::date::CFDate {} impl CFPropertyListSubClass<::number::__CFBoolean> for ::boolean::CFBoolean {} impl CFPropertyListSubClass<::number::__CFNumber> for ::number::CFNumber {} -/// A CFPropertyList struct. This is superclass to [`CFData`], [`CFString`], [`CFArray`], -/// [`CFDictionary`], [`CFDate`], [`CFBoolean`], and [`CFNumber`]. -/// -/// This superclass type does not have its own `CFTypeID`, instead each instance has the `CFTypeID` -/// of the subclass it is an instance of. Thus, this type cannot implement the [`TCFType`] trait, -/// since it cannot implement the static [`TCFType::type_id()`] method. -/// -/// [`CFData`]: ../data/struct.CFData.html -/// [`CFString`]: ../string/struct.CFString.html -/// [`CFArray`]: ../array/struct.CFArray.html -/// [`CFDictionary`]: ../dictionary/struct.CFDictionary.html -/// [`CFDate`]: ../date/struct.CFDate.html -/// [`CFBoolean`]: ../boolean/struct.CFBoolean.html -/// [`CFNumber`]: ../number/struct.CFNumber.html -/// [`TCFType`]: ../base/trait.TCFType.html -/// [`TCFType::type_id()`]: ../base/trait.TCFType.html#method.type_of -pub struct CFPropertyList(CFPropertyListRef); - -impl Drop for CFPropertyList { - fn drop(&mut self) { - unsafe { CFRelease(self.as_CFTypeRef()) } - } + +declare_TCFType!{ + /// A CFPropertyList struct. This is superclass to [`CFData`], [`CFString`], [`CFArray`], + /// [`CFDictionary`], [`CFDate`], [`CFBoolean`], and [`CFNumber`]. + /// + /// This superclass type does not have its own `CFTypeID`, instead each instance has the `CFTypeID` + /// of the subclass it is an instance of. Thus, this type cannot implement the [`TCFType`] trait, + /// since it cannot implement the static [`TCFType::type_id()`] method. + /// + /// [`CFData`]: ../data/struct.CFData.html + /// [`CFString`]: ../string/struct.CFString.html + /// [`CFArray`]: ../array/struct.CFArray.html + /// [`CFDictionary`]: ../dictionary/struct.CFDictionary.html + /// [`CFDate`]: ../date/struct.CFDate.html + /// [`CFBoolean`]: ../boolean/struct.CFBoolean.html + /// [`CFNumber`]: ../number/struct.CFNumber.html + /// [`TCFType`]: ../base/trait.TCFType.html + /// [`TCFType::type_id()`]: ../base/trait.TCFType.html#method.type_of + CFPropertyList, CFPropertyListRef } impl CFPropertyList { diff --git a/core-foundation/src/runloop.rs b/core-foundation/src/runloop.rs index 887303b..5a12c21 100644 --- a/core-foundation/src/runloop.rs +++ b/core-foundation/src/runloop.rs @@ -20,16 +20,8 @@ use string::{CFString}; pub type CFRunLoopMode = CFStringRef; -pub struct CFRunLoop(CFRunLoopRef); - -impl Drop for CFRunLoop { - fn drop(&mut self) { - unsafe { - CFRelease(self.as_CFTypeRef()) - } - } -} +declare_TCFType!(CFRunLoop, CFRunLoopRef); impl_TCFType!(CFRunLoop, CFRunLoopRef, CFRunLoopGetTypeID); impl_CFTypeDescription!(CFRunLoop); @@ -128,16 +120,8 @@ impl CFRunLoop { } -pub struct CFRunLoopTimer(CFRunLoopTimerRef); - -impl Drop for CFRunLoopTimer { - fn drop(&mut self) { - unsafe { - CFRelease(self.as_CFTypeRef()) - } - } -} +declare_TCFType!(CFRunLoopTimer, CFRunLoopTimerRef); impl_TCFType!(CFRunLoopTimer, CFRunLoopTimerRef, CFRunLoopTimerGetTypeID); impl CFRunLoopTimer { @@ -150,29 +134,10 @@ impl CFRunLoopTimer { } -pub struct CFRunLoopSource(CFRunLoopSourceRef); - -impl Drop for CFRunLoopSource { - fn drop(&mut self) { - unsafe { - CFRelease(self.as_CFTypeRef()) - } - } -} - +declare_TCFType!(CFRunLoopSource, CFRunLoopSourceRef); impl_TCFType!(CFRunLoopSource, CFRunLoopSourceRef, CFRunLoopSourceGetTypeID); - -pub struct CFRunLoopObserver(CFRunLoopObserverRef); - -impl Drop for CFRunLoopObserver { - fn drop(&mut self) { - unsafe { - CFRelease(self.as_CFTypeRef()) - } - } -} - +declare_TCFType!(CFRunLoopObserver, CFRunLoopObserverRef); impl_TCFType!(CFRunLoopObserver, CFRunLoopObserverRef, CFRunLoopObserverGetTypeID); #[cfg(test)] diff --git a/core-foundation/src/set.rs b/core-foundation/src/set.rs index 4dab26f..0f3df12 100644 --- a/core-foundation/src/set.rs +++ b/core-foundation/src/set.rs @@ -17,17 +17,11 @@ use base::{CFIndexConvertible, TCFType}; use std::mem; -/// An immutable bag of elements. -pub struct CFSet(CFSetRef); -impl Drop for CFSet { - fn drop(&mut self) { - unsafe { - CFRelease(self.as_CFTypeRef()) - } - } +declare_TCFType!{ + /// An immutable bag of elements. + CFSet, CFSetRef } - impl_TCFType!(CFSet, CFSetRef, CFSetGetTypeID); impl_CFTypeDescription!(CFSet); diff --git a/core-foundation/src/string.rs b/core-foundation/src/string.rs index 5026b71..06cfd56 100644 --- a/core-foundation/src/string.rs +++ b/core-foundation/src/string.rs @@ -20,17 +20,11 @@ use std::str::{self, FromStr}; use std::ptr; use std::ffi::CStr; -/// An immutable string in one of a variety of encodings. -pub struct CFString(CFStringRef); -impl Drop for CFString { - fn drop(&mut self) { - unsafe { - CFRelease(self.as_CFTypeRef()) - } - } +declare_TCFType!{ + /// An immutable string in one of a variety of encodings. + CFString, CFStringRef } - impl_TCFType!(CFString, CFStringRef, CFStringGetTypeID); impl FromStr for CFString { diff --git a/core-foundation/src/timezone.rs b/core-foundation/src/timezone.rs index 5e2650d..596361b 100644 --- a/core-foundation/src/timezone.rs +++ b/core-foundation/src/timezone.rs @@ -18,17 +18,11 @@ use date::{CFDate, CFTimeInterval}; #[cfg(feature = "with-chrono")] use chrono::{FixedOffset, NaiveDateTime}; -/// A time zone. -pub struct CFTimeZone(CFTimeZoneRef); -impl Drop for CFTimeZone { - fn drop(&mut self) { - unsafe { - CFRelease(self.as_CFTypeRef()) - } - } +declare_TCFType!{ + /// A time zone. + CFTimeZone, CFTimeZoneRef } - impl_TCFType!(CFTimeZone, CFTimeZoneRef, CFTimeZoneGetTypeID); impl_CFTypeDescription!(CFTimeZone); diff --git a/core-foundation/src/url.rs b/core-foundation/src/url.rs index 85e05f3..ad77d4a 100644 --- a/core-foundation/src/url.rs +++ b/core-foundation/src/url.rs @@ -27,16 +27,8 @@ use std::os::unix::ffi::OsStrExt; #[cfg(unix)] use std::ffi::OsStr; -pub struct CFURL(CFURLRef); - -impl Drop for CFURL { - fn drop(&mut self) { - unsafe { - CFRelease(self.as_CFTypeRef()) - } - } -} +declare_TCFType!(CFURL, CFURLRef); impl_TCFType!(CFURL, CFURLRef, CFURLGetTypeID); impl fmt::Debug for CFURL { diff --git a/core-foundation/src/uuid.rs b/core-foundation/src/uuid.rs index 3396c16..4aef132 100644 --- a/core-foundation/src/uuid.rs +++ b/core-foundation/src/uuid.rs @@ -20,17 +20,11 @@ use base::TCFType; #[cfg(feature = "with-uuid")] use self::uuid::Uuid; -/// A UUID. -pub struct CFUUID(CFUUIDRef); -impl Drop for CFUUID { - fn drop(&mut self) { - unsafe { - CFRelease(self.as_CFTypeRef()) - } - } +declare_TCFType! { + /// A UUID. + CFUUID, CFUUIDRef } - impl_TCFType!(CFUUID, CFUUIDRef, CFUUIDGetTypeID); impl_CFTypeDescription!(CFUUID); From b13257441d75d0cb715417ebff0381798eaa3dd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Sun, 14 Jan 2018 13:32:15 +0100 Subject: [PATCH 2/5] Add cheap casting between CF types --- core-foundation/src/array.rs | 4 +- core-foundation/src/base.rs | 74 +++++++++++++++++++++++++++++ core-foundation/src/propertylist.rs | 62 ++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 3 deletions(-) diff --git a/core-foundation/src/array.rs b/core-foundation/src/array.rs index e681c68..1fa9c03 100644 --- a/core-foundation/src/array.rs +++ b/core-foundation/src/array.rs @@ -47,9 +47,7 @@ unsafe impl FromVoid for CFType { impl Drop for CFArray { fn drop(&mut self) { - unsafe { - CFRelease(self.as_CFTypeRef()) - } + unsafe { CFRelease(self.as_CFTypeRef()) } } } diff --git a/core-foundation/src/base.rs b/core-foundation/src/base.rs index eb05e34..517f6a5 100644 --- a/core-foundation/src/base.rs +++ b/core-foundation/src/base.rs @@ -8,6 +8,7 @@ // except according to those terms. use std::fmt; +use std::mem; pub use core_foundation_sys::base::*; @@ -35,6 +36,28 @@ declare_TCFType!{ CFType, CFTypeRef } +impl CFType { + pub fn downcast>(&self) -> Option { + if self.instance_of::<_, T>() { + unsafe { + Some(T::wrap_under_get_rule(self.0 as *const Raw)) + } + } else { + None + } + } + + pub fn downcast_into>(self) -> Option { + if self.instance_of::<_, T>() { + let reference = self.0 as *const Raw; + mem::forget(self); + unsafe { Some(T::wrap_under_create_rule(reference)) } + } else { + None + } + } +} + impl fmt::Debug for CFType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let desc = unsafe { @@ -97,6 +120,17 @@ pub trait TCFType { } } + /// Returns the object as a wrapped `CFType`. Consumes self and avoids changing the reference + /// count. + #[inline] + fn into_CFType(self) -> CFType + where Self: Sized + { + let reference = self.as_CFTypeRef(); + mem::forget(self); + unsafe { TCFType::wrap_under_create_rule(reference) } + } + /// Returns the object as a raw `CFTypeRef`. The reference count is not adjusted. fn as_CFTypeRef(&self) -> CFTypeRef; @@ -168,6 +202,7 @@ impl TCFType for CFType { #[cfg(test)] mod tests { use super::*; + use std::mem; use boolean::CFBoolean; #[test] @@ -178,4 +213,43 @@ mod tests { assert!(cftype.instance_of::<_, CFString>()); assert!(!cftype.instance_of::<_, CFBoolean>()); } + + #[test] + fn as_cftype_retain_count() { + let string = CFString::from_static_string("bar"); + assert_eq!(string.retain_count(), 1); + let cftype = string.as_CFType(); + assert_eq!(cftype.retain_count(), 2); + mem::drop(string); + assert_eq!(cftype.retain_count(), 1); + } + + #[test] + fn into_cftype_retain_count() { + let string = CFString::from_static_string("bar"); + assert_eq!(string.retain_count(), 1); + let cftype = string.into_CFType(); + assert_eq!(cftype.retain_count(), 1); + } + + #[test] + fn as_cftype_and_downcast() { + let string = CFString::from_static_string("bar"); + let cftype = string.as_CFType(); + let string2 = cftype.downcast::<_, CFString>().unwrap(); + assert_eq!(string2.to_string(), "bar"); + + assert_eq!(string.retain_count(), 3); + assert_eq!(cftype.retain_count(), 3); + assert_eq!(string2.retain_count(), 3); + } + + #[test] + fn into_cftype_and_downcast_into() { + let string = CFString::from_static_string("bar"); + let cftype = string.into_CFType(); + let string2 = cftype.downcast_into::<_, CFString>().unwrap(); + assert_eq!(string2.to_string(), "bar"); + assert_eq!(string2.retain_count(), 1); + } } diff --git a/core-foundation/src/propertylist.rs b/core-foundation/src/propertylist.rs index ff89e0f..03d5a40 100644 --- a/core-foundation/src/propertylist.rs +++ b/core-foundation/src/propertylist.rs @@ -69,6 +69,14 @@ pub trait CFPropertyListSubClass: TCFType<*const Raw> { fn to_CFPropertyList(&self) -> CFPropertyList { unsafe { CFPropertyList::wrap_under_get_rule(self.as_concrete_TypeRef() as *const c_void) } } + + fn into_CFPropertyList(self) -> CFPropertyList + where Self: Sized + { + let reference = self.as_concrete_TypeRef() as *const c_void; + mem::forget(self); + unsafe { CFPropertyList::wrap_under_create_rule(reference) } + } } impl CFPropertyListSubClass<::data::__CFData> for ::data::CFData {} @@ -117,6 +125,15 @@ impl CFPropertyList { unsafe { CFType::wrap_under_get_rule(self.as_CFTypeRef()) } } + #[inline] + pub fn into_CFType(self) -> CFType + where Self: Sized + { + let reference = self.as_CFTypeRef(); + mem::forget(self); + unsafe { TCFType::wrap_under_create_rule(reference) } + } + #[inline] pub fn as_CFTypeRef(&self) -> ::core_foundation_sys::base::CFTypeRef { unsafe { mem::transmute(self.as_concrete_TypeRef()) } @@ -199,6 +216,17 @@ impl CFPropertyList { None } } + + /// Similar to [`downcast`], but consumes self and can thus avoid touching the retain count. + pub fn downcast_into>(self) -> Option { + if self.instance_of::<_, T>() { + let reference = self.0 as *const Raw; + mem::forget(self); + unsafe { Some(T::wrap_under_create_rule(reference)) } + } else { + None + } + } } @@ -236,6 +264,19 @@ pub mod test { } } + #[test] + fn to_propertylist_retain_count() { + let string = CFString::from_static_string("Bar"); + assert_eq!(string.retain_count(), 1); + + let propertylist = string.to_CFPropertyList(); + assert_eq!(string.retain_count(), 2); + assert_eq!(propertylist.retain_count(), 2); + + mem::drop(string); + assert_eq!(propertylist.retain_count(), 1); + } + #[test] fn downcast_string() { let propertylist = CFString::from_static_string("Bar").to_CFPropertyList(); @@ -249,4 +290,25 @@ pub mod test { assert!(propertylist.downcast::<_, CFBoolean>().is_some()); assert!(propertylist.downcast::<_, CFString>().is_none()); } + + #[test] + fn downcast_into_fail() { + let string = CFString::from_static_string("Bar"); + let propertylist = string.to_CFPropertyList(); + assert_eq!(string.retain_count(), 2); + + assert!(propertylist.downcast_into::<_, CFBoolean>().is_none()); + assert_eq!(string.retain_count(), 1); + } + + #[test] + fn downcast_into() { + let string = CFString::from_static_string("Bar"); + let propertylist = string.to_CFPropertyList(); + assert_eq!(string.retain_count(), 2); + + let string2 = propertylist.downcast_into::<_, CFString>().unwrap(); + assert!(string2.to_string() == "Bar"); + assert_eq!(string2.retain_count(), 2); + } } From 3f4f6a0db2c7f03a5ede3debe982c6361a204210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Sun, 14 Jan 2018 13:57:56 +0100 Subject: [PATCH 3/5] Simplify CFArray test with new downcast method --- core-foundation/src/array.rs | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/core-foundation/src/array.rs b/core-foundation/src/array.rs index 1fa9c03..ced572c 100644 --- a/core-foundation/src/array.rs +++ b/core-foundation/src/array.rs @@ -207,27 +207,25 @@ mod tests { n4.as_CFTypeRef(), n5.as_CFTypeRef()]); - unsafe { - let mut sum = 0; - - let mut iter = arr.iter(); - assert_eq!(iter.len(), 6); - assert!(iter.next().is_some()); - assert_eq!(iter.len(), 5); + let mut sum = 0; - for elem in iter { - let number: CFNumber = TCFType::wrap_under_get_rule(mem::transmute(elem)); - sum += number.to_i64().unwrap() - } + let mut iter = arr.iter(); + assert_eq!(iter.len(), 6); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 5); - assert!(sum == 15); + for elem in iter { + let number: CFNumber = elem.downcast::<_, CFNumber>().unwrap(); + sum += number.to_i64().unwrap() + } - for elem in arr.iter() { - let number: CFNumber = TCFType::wrap_under_get_rule(mem::transmute(elem)); - sum += number.to_i64().unwrap() - } + assert!(sum == 15); - assert!(sum == 30); + for elem in arr.iter() { + let number: CFNumber = elem.downcast::<_, CFNumber>().unwrap(); + sum += number.to_i64().unwrap() } + + assert!(sum == 30); } } From 8a52a343c6cfe275222e9366ccf16dfb718c4750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Mon, 15 Jan 2018 20:42:42 +0100 Subject: [PATCH 4/5] Improve documentation and add #[inline] --- core-foundation/src/base.rs | 28 ++++++++++++++++++++++++++++ core-foundation/src/propertylist.rs | 14 +++++++++++--- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/core-foundation/src/base.rs b/core-foundation/src/base.rs index 517f6a5..768ba2e 100644 --- a/core-foundation/src/base.rs +++ b/core-foundation/src/base.rs @@ -37,6 +37,30 @@ declare_TCFType!{ } impl CFType { + /// Try to downcast the `CFType` to a subclass. Checking if the instance is the + /// correct subclass happens at runtime and `None` is returned if it is not the correct type. + /// Works similar to [`Box::downcast`] and [`CFPropertyList::downcast`]. + /// + /// # Examples + /// + /// ``` + /// # use core_foundation::string::CFString; + /// # use core_foundation::boolean::CFBoolean; + /// # use core_foundation::base::{CFType, TCFType}; + /// # + /// // Create a string. + /// let string: CFString = CFString::from_static_string("FooBar"); + /// // Cast it up to a CFType. + /// let cf_type: CFType = string.as_CFType(); + /// // Cast it down again. + /// assert!(cf_type.downcast::<_, CFString>().unwrap().to_string() == "FooBar"); + /// // Casting it to some other type will yield `None` + /// assert!(cf_type.downcast::<_, CFBoolean>().is_none()); + /// ``` + /// + /// [`Box::downcast`]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast + /// [`CFPropertyList::downcast`]: ../propertylist/struct.CFPropertyList.html#method.downcast + #[inline] pub fn downcast>(&self) -> Option { if self.instance_of::<_, T>() { unsafe { @@ -47,6 +71,10 @@ impl CFType { } } + /// Similar to [`downcast`], but consumes self and can thus avoid touching the retain count. + /// + /// [`downcast`]: #method.downcast + #[inline] pub fn downcast_into>(self) -> Option { if self.instance_of::<_, T>() { let reference = self.0 as *const Raw; diff --git a/core-foundation/src/propertylist.rs b/core-foundation/src/propertylist.rs index 03d5a40..d98f2ec 100644 --- a/core-foundation/src/propertylist.rs +++ b/core-foundation/src/propertylist.rs @@ -66,10 +66,15 @@ pub trait CFPropertyListSubClass: TCFType<*const Raw> { /// Create an instance of the superclass type [`CFPropertyList`] for this instance. /// /// [`CFPropertyList`]: struct.CFPropertyList.html + #[inline] fn to_CFPropertyList(&self) -> CFPropertyList { unsafe { CFPropertyList::wrap_under_get_rule(self.as_concrete_TypeRef() as *const c_void) } } + /// Equal to [`to_CFPropertyList`], but consumes self and avoids changing the reference count. + /// + /// [`to_CFPropertyList`]: #method.to_CFPropertyList + #[inline] fn into_CFPropertyList(self) -> CFPropertyList where Self: Sized { @@ -189,9 +194,9 @@ impl PartialEq for CFPropertyList { impl Eq for CFPropertyList {} impl CFPropertyList { - /// Try to downcast the [`CFPropertyList`] to a subclass. Checking if the instance is the correct - /// subclass happens at runtime and an error is returned if it is not the correct type. - /// Works similar to [`Box::downcast`]. + /// Try to downcast the [`CFPropertyList`] to a subclass. Checking if the instance is the + /// correct subclass happens at runtime and `None` is returned if it is not the correct type. + /// Works similar to [`Box::downcast`] and [`CFType::downcast`]. /// /// # Examples /// @@ -209,6 +214,7 @@ impl CFPropertyList { /// /// [`CFPropertyList`]: struct.CFPropertyList.html /// [`Box::downcast`]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast + /// [`CFType::downcast`]: ../base/struct.CFType.html#method.downcast pub fn downcast>(&self) -> Option { if self.instance_of::<_, T>() { Some(unsafe { T::wrap_under_get_rule(self.0 as *const Raw) }) @@ -218,6 +224,8 @@ impl CFPropertyList { } /// Similar to [`downcast`], but consumes self and can thus avoid touching the retain count. + /// + /// [`downcast`]: #method.downcast pub fn downcast_into>(self) -> Option { if self.instance_of::<_, T>() { let reference = self.0 as *const Raw; From 3ec0d578630e142a26150cf7c2c9e07d362eb2d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Sat, 20 Jan 2018 16:38:58 +0100 Subject: [PATCH 5/5] Convert CFMutableDictionary to use declare_TCFType --- core-foundation/src/dictionary.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/core-foundation/src/dictionary.rs b/core-foundation/src/dictionary.rs index c4c2f7c..ba5fc67 100644 --- a/core-foundation/src/dictionary.rs +++ b/core-foundation/src/dictionary.rs @@ -121,15 +121,9 @@ impl CFDictionary { } } -/// An mutable dictionary of key-value pairs. -pub struct CFMutableDictionary(CFMutableDictionaryRef); - -impl Drop for CFMutableDictionary { - fn drop(&mut self) { - unsafe { - CFRelease(self.as_CFTypeRef()) - } - } +declare_TCFType!{ + /// An mutable dictionary of key-value pairs. + CFMutableDictionary, CFMutableDictionaryRef } impl_TCFType!(CFMutableDictionary, CFMutableDictionaryRef, CFDictionaryGetTypeID);