Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a few helper functions #116

Merged
merged 4 commits into from
Jan 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions objc2-foundation/src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub unsafe trait INSObject: Message {

fn is_equal<T: INSObject>(&self, other: &T) -> bool {
let result: Bool = unsafe { msg_send![self, isEqual: other] };
result.is_true()
result.as_bool()
}

fn description(&self) -> Id<NSString, Shared> {
Expand All @@ -30,7 +30,7 @@ pub unsafe trait INSObject: Message {

fn is_kind_of(&self, cls: &Class) -> bool {
let result: Bool = unsafe { msg_send![self, isKindOfClass: cls] };
result.is_true()
result.as_bool()
}
}

Expand Down
3 changes: 3 additions & 0 deletions objc2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* Added deprecated `Object::get_ivar` and `Object::get_mut_ivar` to make
upgrading easier.
* Allow using `From`/`TryFrom` to convert between `rc::Id` and `rc::WeakId`.
* Added `Bool::as_bool` (more descriptive name than `Bool::is_true`).
* Added convenience methods `Id::new_null`, `Id::as_ptr` and
`Id::retain_null`.


## 0.3.0-alpha.6 - 2022-01-03
Expand Down
28 changes: 22 additions & 6 deletions objc2/src/bool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use core::fmt;
/// The Objective-C `BOOL` type.
///
/// This is a thin wrapper-type over [`objc_sys::BOOL`]. It is intended that
/// you convert this into a Rust [`bool`] with the [`Bool::is_false`] or
/// [`Bool::is_true`] methods as soon as possible.
/// you convert this into a Rust [`bool`] with the [`Bool::as_bool`] method as
/// soon as possible.
///
/// This is FFI-safe and can be used in directly with
/// [`msg_send!`][`crate::msg_send`].
Expand All @@ -20,12 +20,12 @@ use core::fmt;
/// use objc2::runtime::{Object, Bool};
/// let ns_value: *mut Object = unsafe { msg_send![class!(NSValue), initWithBool: Bool::YES] };
/// let rtn: Bool = unsafe { msg_send![ns_value, boolValue] };
/// assert!(rtn.is_true());
/// assert!(rtn.as_bool());
/// ```
#[repr(transparent)]
// We don't implement comparison traits because they could be implemented with
// two slightly different semantics:
// - `self.is_true().cmp(other.is_true())`
// - `self.as_bool().cmp(other.as_bool())`
// - `self.value.cmp(other.value)`
// And it is not immediately clear for users which one was chosen.
#[derive(Copy, Clone, Default)]
Expand Down Expand Up @@ -63,6 +63,8 @@ impl Bool {
}

/// Returns `true` if `self` is [`NO`][`Self::NO`].
///
/// You should prefer using [`as_bool`][`Self::as_bool`].
#[inline]
pub const fn is_false(self) -> bool {
// Always compare with 0
Expand All @@ -71,12 +73,20 @@ impl Bool {
}

/// Returns `true` if `self` is the opposite of [`NO`][`Self::NO`].
///
/// You should prefer using [`as_bool`][`Self::as_bool`].
#[inline]
pub const fn is_true(self) -> bool {
// Always compare with 0
// This is what happens when using `if` in C.
self.value as u8 != 0
}

/// Converts this into the [`bool`] equivalent.
#[inline]
pub const fn as_bool(self) -> bool {
self.is_true()
}
}

impl From<bool> for Bool {
Expand All @@ -89,13 +99,13 @@ impl From<bool> for Bool {
impl From<Bool> for bool {
#[inline]
fn from(b: Bool) -> bool {
b.is_true()
b.as_bool()
}
}

impl fmt::Debug for Bool {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(if self.is_true() { "YES" } else { "NO" })
f.write_str(if self.as_bool() { "YES" } else { "NO" })
}
}

Expand Down Expand Up @@ -125,12 +135,14 @@ mod tests {
#[test]
fn test_basic() {
let b = Bool::new(true);
assert!(b.as_bool());
assert!(b.is_true());
assert!(!b.is_false());
assert!(bool::from(b));
assert_eq!(b.as_raw() as usize, 1);

let b = Bool::new(false);
assert!(!b.as_bool());
assert!(!b.is_true());
assert!(b.is_false());
assert!(!bool::from(b));
Expand All @@ -140,19 +152,23 @@ mod tests {
#[test]
fn test_associated_constants() {
let b = Bool::YES;
assert!(b.as_bool());
assert!(b.is_true());
assert_eq!(b.as_raw() as usize, 1);

let b = Bool::NO;
assert!(!b.as_bool());
assert!(b.is_false());
assert_eq!(b.as_raw() as usize, 0);
}

#[test]
fn test_impls() {
let b: Bool = Default::default();
assert!(!b.as_bool());
assert!(b.is_false());

assert!(Bool::from(true).as_bool());
assert!(Bool::from(true).is_true());
assert!(Bool::from(false).is_false());

Expand Down
8 changes: 4 additions & 4 deletions objc2/src/declare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ impl ClassDecl {
types.as_ptr(),
)
});
assert!(success.is_true(), "Failed to add method {:?}", sel);
assert!(success.as_bool(), "Failed to add method {:?}", sel);
}

/// Adds a class method with the given name and implementation.
Expand Down Expand Up @@ -253,7 +253,7 @@ impl ClassDecl {
types.as_ptr(),
)
});
assert!(success.is_true(), "Failed to add class method {:?}", sel);
assert!(success.as_bool(), "Failed to add class method {:?}", sel);
}

/// Adds an ivar with type `T` and the provided name.
Expand All @@ -275,7 +275,7 @@ impl ClassDecl {
encoding.as_ptr(),
)
});
assert!(success.is_true(), "Failed to add ivar {}", name);
assert!(success.as_bool(), "Failed to add ivar {}", name);
}

/// Adds the given protocol to self.
Expand All @@ -285,7 +285,7 @@ impl ClassDecl {
/// If the protocol wasn't successfully added.
pub fn add_protocol(&mut self, proto: &Protocol) {
let success = unsafe { ffi::class_addProtocol(self.cls as _, proto.as_ptr()) };
let success = Bool::from_raw(success).is_true();
let success = Bool::from_raw(success).as_bool();
assert!(success, "Failed to add protocol {:?}", proto);
}

Expand Down
2 changes: 1 addition & 1 deletion objc2/src/exception.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ extern "C" {
#[inline]
pub unsafe fn throw(exception: Option<&Id<Object, Shared>>) -> ! {
let exception = match exception {
Some(id) => &**id as *const Object as *mut ffi::objc_object,
Some(id) => id.as_ptr() as *mut ffi::objc_object,
None => ptr::null_mut(),
};
unsafe { ffi::objc_exception_throw(exception) }
Expand Down
2 changes: 1 addition & 1 deletion objc2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
//! // Usage
//! let hash: NSUInteger = unsafe { msg_send![obj, hash] };
//! let is_kind: Bool = unsafe { msg_send![obj, isKindOfClass: cls] };
//! assert!(is_kind.is_true());
//! assert!(is_kind.as_bool());
//! ```
//!
//! Note that this very simple example contains **a lot** of `unsafe` (which
Expand Down
5 changes: 2 additions & 3 deletions objc2/src/message/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,7 @@ unsafe impl<T: Message + ?Sized> MessageReceiver for NonNull<T> {
unsafe impl<T: Message + ?Sized, O: Ownership> MessageReceiver for Id<T, O> {
#[inline]
fn as_raw_receiver(&self) -> *mut Object {
// TODO: Maybe don't dereference here, just to be safe?
(&**self).as_raw_receiver()
self.as_ptr() as *mut Object
}
}

Expand Down Expand Up @@ -449,7 +448,7 @@ mod tests {
};
assert_eq!(result, 4);

let obj: *const ManuallyDrop<Object> = (&**obj as *const Object).cast();
let obj: *const ManuallyDrop<Object> = obj.as_ptr().cast();
let result: u32 = unsafe { msg_send![obj, foo] };
assert_eq!(result, 4);
}
Expand Down
44 changes: 43 additions & 1 deletion objc2/src/rc/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,33 @@ impl<T: Message + ?Sized, O: Ownership> Id<T, O> {
own: PhantomData,
}
}

/// Constructs an [`Id`] from a pointer that may be null.
///
/// This is just a convenience wrapper over [`Id::new`] so that you don't
/// need to construct a [`NonNull`] when you know the pointer may be null.
///
/// # Safety
///
/// Same as [`Id::new`].
#[inline]
pub unsafe fn new_null(ptr: *mut T) -> Option<Id<T, O>> {
// SAFETY: Upheld by the caller
NonNull::new(ptr).map(|ptr| unsafe { Id::new(ptr) })
}

/// Returns a raw pointer to the object.
///
/// The pointer is valid for at least as long as the `Id` is held.
#[inline]
pub fn as_ptr(&self) -> *mut T {
// Note: This is not an associated function, which breaks the
// guideline that smart pointers shouldn't add inherent methods!
//
// However, this method is quite useful when migrating old codebases,
// so I think we'll let it be here for now.
self.ptr.as_ptr()
}
}

// TODO: Add ?Sized bound
Expand Down Expand Up @@ -224,6 +251,21 @@ impl<T: Message, O: Ownership> Id<T, O> {
unsafe { Self::new(res) }
}

/// Retains an object pointer that may be null.
///
/// This is just a convenience wrapper over [`Id::retain`] so that you
/// don't need to construct a [`NonNull`] when you know the pointer may
/// be null.
///
/// # Safety
///
/// Same as [`Id::retain`].
#[inline]
pub unsafe fn retain_null(ptr: *mut T) -> Option<Id<T, O>> {
// SAFETY: Upheld by the caller
NonNull::new(ptr).map(|ptr| unsafe { Id::retain(ptr) })
}

#[cfg_attr(not(debug_assertions), inline)]
fn autorelease_inner(self) -> *mut T {
// Note that this (and the actual `autorelease`) is not an associated
Expand All @@ -232,7 +274,7 @@ impl<T: Message, O: Ownership> Id<T, O> {
// retained objects it is hard to imagine a case where the inner type
// has a method with the same name.

let ptr = ManuallyDrop::new(self).ptr.as_ptr() as *mut ffi::objc_object;
let ptr = ManuallyDrop::new(self).as_ptr() as *mut ffi::objc_object;
// SAFETY: The `ptr` is guaranteed to be valid and have at least one
// retain count.
// And because of the ManuallyDrop, we don't call the Drop
Expand Down
2 changes: 1 addition & 1 deletion objc2/src/rc/weak_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ impl<T: Message> WeakId<T> {
// allow loading an `Id<T, Shared>` later on.

// SAFETY: `obj` is valid
unsafe { Self::new_inner(&**obj as *const T as *mut T) }
unsafe { Self::new_inner(obj.as_ptr()) }
}

/// # Safety
Expand Down
8 changes: 4 additions & 4 deletions objc2/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ impl Class {

#[allow(unused)]
fn is_metaclass(&self) -> bool {
unsafe { Bool::from_raw(ffi::class_isMetaClass(self.as_ptr())).is_true() }
unsafe { Bool::from_raw(ffi::class_isMetaClass(self.as_ptr())).as_bool() }
}

/// Returns the size of instances of self.
Expand Down Expand Up @@ -378,7 +378,7 @@ impl Class {
/// Checks whether this class conforms to the specified protocol.
pub fn conforms_to(&self, proto: &Protocol) -> bool {
unsafe {
Bool::from_raw(ffi::class_conformsToProtocol(self.as_ptr(), proto.as_ptr())).is_true()
Bool::from_raw(ffi::class_conformsToProtocol(self.as_ptr(), proto.as_ptr())).as_bool()
}
}

Expand Down Expand Up @@ -478,7 +478,7 @@ impl Protocol {
self.as_ptr(),
proto.as_ptr(),
))
.is_true()
.as_bool()
}
}

Expand All @@ -493,7 +493,7 @@ impl PartialEq for Protocol {
/// Check whether the protocols are equal, or conform to each other.
#[inline]
fn eq(&self, other: &Protocol) -> bool {
unsafe { Bool::from_raw(ffi::protocol_isEqual(self.as_ptr(), other.as_ptr())).is_true() }
unsafe { Bool::from_raw(ffi::protocol_isEqual(self.as_ptr(), other.as_ptr())).as_bool() }
}
}

Expand Down