Skip to content

Commit a680abd

Browse files
authored
Merge pull request #16 from madsmtm/bool
Add Bool (newtype wrapper over BOOL)
2 parents ecea206 + 52b58a6 commit a680abd

File tree

9 files changed

+210
-41
lines changed

9 files changed

+210
-41
lines changed

objc2/src/bool.rs

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
use crate::{Encode, Encoding, RefEncode};
2+
use core::fmt;
3+
4+
/// The Objective-C `BOOL` type.
5+
///
6+
/// This is a thin wrapper-type over [`objc2_sys::BOOL`]. It is intended that
7+
/// you convert this into a Rust [`bool`] with the [`Bool::is_false`] or
8+
/// [`Bool::is_true`] methods as soon as possible.
9+
///
10+
/// This is FFI-safe and can be used in directly with
11+
/// [`msg_send!`][`crate::msg_send`].
12+
///
13+
/// Note that this is able to contain more states than `bool` on some
14+
/// platforms, but these cases should not be relied on!
15+
///
16+
/// # Example
17+
///
18+
/// ```no_run
19+
/// use objc2::{class, msg_send};
20+
/// use objc2::runtime::{Object, Bool};
21+
/// let ns_value: *mut Object = unsafe { msg_send![class!(NSValue), initWithBool: Bool::YES] };
22+
/// let rtn: Bool = unsafe { msg_send![ns_value, boolValue] };
23+
/// assert!(rtn.is_true());
24+
/// ```
25+
#[repr(transparent)]
26+
// We don't implement comparison traits because they could be implemented with
27+
// two slightly different semantics:
28+
// - `self.is_true().cmp(other.is_true())`
29+
// - `self.value.cmp(other.value)`
30+
// And it is not immediately clear for users which one was chosen.
31+
#[derive(Copy, Clone, Default)]
32+
pub struct Bool {
33+
value: objc2_sys::BOOL,
34+
}
35+
36+
impl Bool {
37+
/// The equivalent of [`true`] for Objective-C's `BOOL` type.
38+
pub const YES: Self = Self::from_raw(objc2_sys::YES);
39+
40+
/// The equivalent of [`false`] for Objective-C's `BOOL` type.
41+
pub const NO: Self = Self::from_raw(objc2_sys::NO);
42+
43+
/// Creates an Objective-C boolean from a Rust boolean.
44+
#[inline]
45+
pub const fn new(value: bool) -> Self {
46+
// true as u8 => 1
47+
// false as u8 => 0
48+
let value = value as objc2_sys::BOOL;
49+
Self { value }
50+
}
51+
52+
/// Creates this from a boolean value received from a raw Objective-C API.
53+
#[inline]
54+
pub const fn from_raw(value: objc2_sys::BOOL) -> Self {
55+
Self { value }
56+
}
57+
58+
/// Retrieves the inner [`objc2_sys`] boolean type, to be used in raw
59+
/// Objective-C APIs.
60+
#[inline]
61+
pub const fn as_raw(self) -> objc2_sys::BOOL {
62+
self.value
63+
}
64+
65+
/// Returns `true` if `self` is [`NO`][`Self::NO`].
66+
#[inline]
67+
pub const fn is_false(self) -> bool {
68+
// Always compare with 0
69+
// This is what happens with the `!` operator in C.
70+
self.value as u8 == 0
71+
}
72+
73+
/// Returns `true` if `self` is the opposite of [`NO`][`Self::NO`].
74+
#[inline]
75+
pub const fn is_true(self) -> bool {
76+
// Always compare with 0
77+
// This is what happens when using `if` in C.
78+
self.value as u8 != 0
79+
}
80+
}
81+
82+
impl From<bool> for Bool {
83+
#[inline]
84+
fn from(b: bool) -> Bool {
85+
Bool::new(b)
86+
}
87+
}
88+
89+
impl From<Bool> for bool {
90+
#[inline]
91+
fn from(b: Bool) -> bool {
92+
b.is_true()
93+
}
94+
}
95+
96+
impl fmt::Debug for Bool {
97+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
98+
f.write_str(if self.is_true() { "YES" } else { "NO" })
99+
}
100+
}
101+
102+
unsafe impl Encode for Bool {
103+
const ENCODING: Encoding<'static> = objc2_sys::BOOL::ENCODING;
104+
}
105+
106+
unsafe impl RefEncode for Bool {
107+
const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
108+
}
109+
110+
#[cfg(test)]
111+
mod tests {
112+
use super::*;
113+
114+
#[test]
115+
fn test_basic() {
116+
let b = Bool::new(true);
117+
assert!(b.is_true());
118+
assert!(!b.is_false());
119+
assert!(bool::from(b));
120+
assert_eq!(b.as_raw() as usize, 1);
121+
122+
let b = Bool::new(false);
123+
assert!(!b.is_true());
124+
assert!(b.is_false());
125+
assert!(!bool::from(b));
126+
assert_eq!(b.as_raw() as usize, 0);
127+
}
128+
129+
#[test]
130+
fn test_associated_constants() {
131+
let b = Bool::YES;
132+
assert!(b.is_true());
133+
assert_eq!(b.as_raw() as usize, 1);
134+
135+
let b = Bool::NO;
136+
assert!(b.is_false());
137+
assert_eq!(b.as_raw() as usize, 0);
138+
}
139+
140+
#[test]
141+
fn test_impls() {
142+
let b: Bool = Default::default();
143+
assert!(b.is_false());
144+
145+
assert!(Bool::from(true).is_true());
146+
assert!(Bool::from(false).is_false());
147+
148+
assert!(Bool::from(true).is_true());
149+
assert!(Bool::from(false).is_false());
150+
}
151+
152+
// Can't really do this test since it won't compile on platforms where
153+
// type BOOL = bool.
154+
//
155+
// #[test]
156+
// fn test_outside_normal() {
157+
// let b = Bool::from_raw(42);
158+
// assert!(b.is_true());
159+
// assert!(!b.is_false());
160+
// assert_eq!(b.as_raw(), 42);
161+
// }
162+
}

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).as_raw(),
327+
Bool::new(is_instance_method).as_raw(),
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: 8 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())).is_true() }
296297
}
297298

298299
/// Get a list of the protocols to which this class conforms.
@@ -367,7 +368,9 @@ 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 {
372+
Bool::from_raw(protocol_conformsToProtocol(self.as_ptr(), proto.as_ptr())).is_true()
373+
}
371374
}
372375

373376
/// Returns the name of self.
@@ -379,7 +382,7 @@ impl Protocol {
379382

380383
impl PartialEq for Protocol {
381384
fn eq(&self, other: &Protocol) -> bool {
382-
unsafe { protocol_isEqual(self.as_ptr(), other.as_ptr()) == YES }
385+
unsafe { Bool::from_raw(protocol_isEqual(self.as_ptr(), other.as_ptr())).is_true() }
383386
}
384387
}
385388

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.")]

0 commit comments

Comments
 (0)