Skip to content

Commit fddce93

Browse files
committed
Add Bool newtype wrapper
BOOL differs across platforms, and usage requires a comparison to NO, so while this is a bit cumbersome, it's really the best way to ensure that they are handled properly. Using the Encode impl for the built in `bool` is heavily discouraged, since that can easily lead to errors!
1 parent ecea206 commit fddce93

File tree

8 files changed

+150
-41
lines changed

8 files changed

+150
-41
lines changed

objc2/src/bool.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
use objc2_sys::BOOL;
2+
3+
use crate::{Encode, Encoding, RefEncode};
4+
use core::fmt;
5+
6+
/// The Objective-C `BOOL` type.
7+
///
8+
/// To convert an Objective-C `BOOL` into a Rust [`bool`], call the one of the
9+
/// [`Bool::is_false`] or [`Bool::is_true`] methods.
10+
///
11+
/// This is FFI-safe and can be used in directly with
12+
/// [`msg_send!`][`crate::msg_send`].
13+
#[repr(transparent)]
14+
// TODO: Might have to implement some of these manually, in case someone puts
15+
// something that is not 0 or 1 into the Bool?
16+
#[derive(Copy, Clone, Default, Ord, PartialOrd, Eq, PartialEq, Hash)]
17+
pub struct Bool {
18+
value: BOOL,
19+
}
20+
21+
impl Bool {
22+
/// The equivalent of [`true`] for Objective-C's `BOOL` type.
23+
pub const YES: Self = Self::new(true);
24+
25+
/// The equivalent of [`false`] for Objective-C's `BOOL` type.
26+
pub const NO: Self = Self::new(false);
27+
28+
/// Creates an Objective-C boolean from a Rust boolean.
29+
#[inline]
30+
pub const fn new(value: bool) -> Self {
31+
// true as u8 => 1
32+
// false as u8 => 0
33+
let value = value as BOOL;
34+
Self { value }
35+
}
36+
37+
/// Creates this from a boolean value received from a raw Objective-C API.
38+
#[inline]
39+
pub const fn from_raw(value: BOOL) -> Self {
40+
Self { value }
41+
}
42+
43+
/// Retrieves the inner `objc2_sys` boolean type, to be used in raw
44+
/// Objective-C APIs.
45+
#[inline]
46+
pub const fn as_raw(self) -> BOOL {
47+
self.value
48+
}
49+
50+
/// Returns `true` if `self` is [`NO`][`Self::NO`].
51+
#[inline]
52+
pub const fn is_false(self) -> bool {
53+
// Always compare with 0
54+
// This is what happens with the `!` operator in C.
55+
self.value as u8 == 0
56+
}
57+
58+
/// Returns `true` if `self` is the opposite of [`NO`][`Self::NO`].
59+
#[inline]
60+
pub const fn is_true(self) -> bool {
61+
// Always compare with 0
62+
// This is what happens when using `if` in C.
63+
self.value as u8 != 0
64+
}
65+
}
66+
67+
impl From<BOOL> for Bool {
68+
#[inline]
69+
fn from(b: BOOL) -> Bool {
70+
Bool::from_raw(b)
71+
}
72+
}
73+
74+
impl From<Bool> for BOOL {
75+
#[inline]
76+
fn from(b: Bool) -> BOOL {
77+
b.as_raw()
78+
}
79+
}
80+
81+
impl From<bool> for Bool {
82+
#[inline]
83+
fn from(b: bool) -> Bool {
84+
Bool::new(b)
85+
}
86+
}
87+
88+
impl From<Bool> for bool {
89+
#[inline]
90+
fn from(b: Bool) -> bool {
91+
b.is_true()
92+
}
93+
}
94+
95+
impl fmt::Debug for Bool {
96+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
97+
f.write_str(if self.is_true() { "YES" } else { "NO" })
98+
}
99+
}
100+
101+
unsafe impl Encode for Bool {
102+
const ENCODING: Encoding<'static> = BOOL::ENCODING;
103+
}
104+
105+
unsafe impl RefEncode for Bool {
106+
const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
107+
}

objc2/src/declare.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use core::mem;
3838
use core::ptr;
3939
use std::ffi::CString;
4040

41-
use crate::runtime::{self, Class, Imp, Object, Protocol, Sel, BOOL, NO};
41+
use crate::runtime::{self, Bool, Class, Imp, Object, Protocol, Sel};
4242
use crate::{Encode, EncodeArguments, Encoding, Message};
4343

4444
/// Types that can be used as the implementation of an Objective-C method.
@@ -182,13 +182,13 @@ impl ClassDecl {
182182
);
183183

184184
let types = method_type_encoding(&F::Ret::ENCODING, encs);
185-
let success = runtime::class_addMethod(
185+
let success = Bool::from_raw(runtime::class_addMethod(
186186
self.cls as _,
187187
sel.as_ptr() as _,
188188
Some(func.imp()),
189189
types.as_ptr(),
190-
);
191-
assert!(success != NO, "Failed to add method {:?}", sel);
190+
));
191+
assert!(success.is_true(), "Failed to add method {:?}", sel);
192192
}
193193

194194
/// Adds a class method with the given name and implementation.
@@ -217,13 +217,13 @@ impl ClassDecl {
217217

218218
let types = method_type_encoding(&F::Ret::ENCODING, encs);
219219
let metaclass = (*self.cls).metaclass() as *const _ as *mut _;
220-
let success = runtime::class_addMethod(
220+
let success = Bool::from_raw(runtime::class_addMethod(
221221
metaclass,
222222
sel.as_ptr() as _,
223223
Some(func.imp()),
224224
types.as_ptr(),
225-
);
226-
assert!(success != NO, "Failed to add class method {:?}", sel);
225+
));
226+
assert!(success.is_true(), "Failed to add class method {:?}", sel);
227227
}
228228

229229
/// Adds an ivar with type `T` and the provided name.
@@ -236,16 +236,16 @@ impl ClassDecl {
236236
let encoding = CString::new(T::ENCODING.to_string()).unwrap();
237237
let size = mem::size_of::<T>();
238238
let align = log2_align_of::<T>();
239-
let success = unsafe {
239+
let success = Bool::from_raw(unsafe {
240240
runtime::class_addIvar(
241241
self.cls as _,
242242
c_name.as_ptr(),
243243
size,
244244
align,
245245
encoding.as_ptr(),
246246
)
247-
};
248-
assert!(success != NO, "Failed to add ivar {}", name);
247+
});
248+
assert!(success.is_true(), "Failed to add ivar {}", name);
249249
}
250250

251251
/// Adds the given protocol to self.
@@ -255,7 +255,8 @@ impl ClassDecl {
255255
/// If the protocol wasn't successfully added.
256256
pub fn add_protocol(&mut self, proto: &Protocol) {
257257
let success = unsafe { runtime::class_addProtocol(self.cls as _, proto.as_ptr()) };
258-
assert!(success != NO, "Failed to add protocol {:?}", proto);
258+
let success = Bool::from_raw(success).is_true();
259+
assert!(success, "Failed to add protocol {:?}", proto);
259260
}
260261

261262
/// Registers the [`ClassDecl`], consuming it, and returns a reference to
@@ -322,8 +323,8 @@ impl ProtocolDecl {
322323
self.proto as _,
323324
sel.as_ptr() as _,
324325
types.as_ptr(),
325-
is_required as BOOL,
326-
is_instance_method as BOOL,
326+
Bool::new(is_required).into(),
327+
Bool::new(is_instance_method).into(),
327328
);
328329
}
329330
}

objc2/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ pub use crate::cache::CachedSel as __CachedSel;
8989
#[macro_use]
9090
mod macros;
9191

92+
mod bool;
9293
mod cache;
9394
pub mod declare;
9495
mod encode;

objc2/src/runtime.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ use malloc_buf::Malloc;
1111
use std::ffi::{CStr, CString};
1212
use std::os::raw::c_uint;
1313

14-
use crate::Encode;
15-
1614
#[allow(deprecated)]
1715
pub use objc2_sys::object_dispose;
1816
pub use objc2_sys::{
@@ -32,6 +30,9 @@ pub use objc2_sys::{
3230
};
3331
pub use objc2_sys::{BOOL, NO, YES};
3432

33+
pub use super::bool::Bool;
34+
use crate::Encode;
35+
3536
/// A type that represents a method selector.
3637
#[repr(transparent)]
3738
pub struct Sel {
@@ -292,7 +293,7 @@ impl Class {
292293

293294
/// Checks whether this class conforms to the specified protocol.
294295
pub fn conforms_to(&self, proto: &Protocol) -> bool {
295-
unsafe { class_conformsToProtocol(self.as_ptr(), proto.as_ptr()) == YES }
296+
unsafe { Bool::from_raw(class_conformsToProtocol(self.as_ptr(), proto.as_ptr())).into() }
296297
}
297298

298299
/// Get a list of the protocols to which this class conforms.
@@ -367,7 +368,7 @@ impl Protocol {
367368

368369
/// Checks whether this protocol conforms to the specified protocol.
369370
pub fn conforms_to(&self, proto: &Protocol) -> bool {
370-
unsafe { protocol_conformsToProtocol(self.as_ptr(), proto.as_ptr()) == YES }
371+
unsafe { Bool::from_raw(protocol_conformsToProtocol(self.as_ptr(), proto.as_ptr())).into() }
371372
}
372373

373374
/// Returns the name of self.
@@ -379,7 +380,7 @@ impl Protocol {
379380

380381
impl PartialEq for Protocol {
381382
fn eq(&self, other: &Protocol) -> bool {
382-
unsafe { protocol_isEqual(self.as_ptr(), other.as_ptr()) == YES }
383+
unsafe { Bool::from_raw(protocol_isEqual(self.as_ptr(), other.as_ptr())).is_true() }
383384
}
384385
}
385386

objc2_encode/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,23 +28,23 @@ use objc2_encode::{Encode, Encoding, RefEncode};
2828
#[repr(C)]
2929
struct MyObject {
3030
a: f32,
31-
b: bool,
31+
b: i16,
3232
}
3333

3434
unsafe impl Encode for MyObject {
3535
const ENCODING: Encoding<'static> = Encoding::Struct(
3636
"MyObject",
37-
&[f32::ENCODING, bool::ENCODING
38-
]);
37+
&[f32::ENCODING, i16::ENCODING],
38+
);
3939
}
4040

41-
assert_eq!(&MyObject::ENCODING, "{MyObject=fB}");
41+
assert_eq!(&MyObject::ENCODING, "{MyObject=fs}");
4242

4343
unsafe impl RefEncode for MyObject {
4444
const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
4545
}
4646

47-
assert_eq!(&MyObject::ENCODING_REF, "^{MyObject=fB}");
47+
assert_eq!(&MyObject::ENCODING_REF, "^{MyObject=fs}");
4848
```
4949

5050
An `Encoding` can be compared with an encoding string from the Objective-C

objc2_encode/src/encode.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use crate::Encoding;
3737
/// #[repr(C)]
3838
/// struct MyType {
3939
/// a: i32,
40-
/// b: bool,
40+
/// b: f64,
4141
/// c: *const c_void,
4242
/// }
4343
///
@@ -49,7 +49,7 @@ use crate::Encoding;
4949
/// // Delegate to field's implementations.
5050
/// // The order is the same as in the definition.
5151
/// i32::ENCODING,
52-
/// bool::ENCODING,
52+
/// f64::ENCODING,
5353
/// <*const c_void>::ENCODING,
5454
/// ],
5555
/// );
@@ -160,14 +160,21 @@ encode_impls!(
160160
u64 => ULongLong,
161161
f32 => Float,
162162
f64 => Double,
163-
bool => Bool,
164163
() => Void,
165164
*mut i8 => String,
166165
*const i8 => String,
167166
*mut u8 => String,
168167
*const u8 => String,
169168
);
170169

170+
/// Using this directly is heavily discouraged, since the type of BOOL differs
171+
/// across platforms.
172+
///
173+
/// Use `objc2_sys::BOOL::ENCODING` or `objc2::runtime::Bool` instead.
174+
unsafe impl Encode for bool {
175+
const ENCODING: Encoding<'static> = Encoding::Bool;
176+
}
177+
171178
macro_rules! encode_impls_size {
172179
($($t:ty => ($t16:ty, $t32:ty, $t64:ty),)*) => ($(
173180
#[doc = concat!("The encoding of [`", stringify!($t), "`] varies based on the target pointer width.")]

objc2_foundation/src/object.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use core::ptr::NonNull;
33

44
use objc2::msg_send;
55
use objc2::rc::{Id, Owned, Shared};
6-
use objc2::runtime::{Class, BOOL, NO};
6+
use objc2::runtime::{Bool, Class};
77
use objc2::Message;
88

99
use super::NSString;
@@ -25,8 +25,8 @@ pub trait INSObject: Any + Sized + Message {
2525
where
2626
T: INSObject,
2727
{
28-
let result: BOOL = unsafe { msg_send![self, isEqual: other] };
29-
result != NO
28+
let result: Bool = unsafe { msg_send![self, isEqual: other] };
29+
result.is_true()
3030
}
3131

3232
fn description(&self) -> Id<NSString, Shared> {
@@ -38,8 +38,8 @@ pub trait INSObject: Any + Sized + Message {
3838
}
3939

4040
fn is_kind_of(&self, cls: &Class) -> bool {
41-
let result: BOOL = unsafe { msg_send![self, isKindOfClass: cls] };
42-
result != NO
41+
let result: Bool = unsafe { msg_send![self, isKindOfClass: cls] };
42+
result.is_true()
4343
}
4444

4545
fn new() -> Id<Self, Owned> {

objc2_sys/src/constants.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,11 @@ use std::os::raw::c_int;
55

66
use crate::{id, Class, BOOL};
77

8-
#[cfg(not(target_arch = "aarch64"))]
98
/// The equivalent of `true` for Objective-C's [`BOOL`][`super::BOOL`] type.
10-
pub const YES: BOOL = 1;
11-
#[cfg(target_arch = "aarch64")]
12-
/// The equivalent of `true` for Objective-C's [`BOOL`][`super::BOOL`] type.
13-
pub const YES: BOOL = true;
9+
pub const YES: BOOL = true as BOOL;
1410

15-
#[cfg(not(target_arch = "aarch64"))]
16-
/// The equivalent of `false` for Objective-C's [`BOOL`][`super::BOOL`] type.
17-
pub const NO: BOOL = 0;
18-
#[cfg(target_arch = "aarch64")]
1911
/// The equivalent of `false` for Objective-C's [`BOOL`][`super::BOOL`] type.
20-
pub const NO: BOOL = false;
12+
pub const NO: BOOL = false as BOOL;
2113

2214
/// A quick alias for a [`null_mut`][`core::ptr::null_mut`] object / instance.
2315
pub const nil: id = 0 as *mut _;

0 commit comments

Comments
 (0)