Skip to content

Commit 1ddd898

Browse files
authored
Merge pull request #52 from madsmtm/more-encode
More `Encode` and `RefEncode` implementations
2 parents 4060608 + 6112808 commit 1ddd898

File tree

6 files changed

+154
-16
lines changed

6 files changed

+154
-16
lines changed

objc2/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ Objective-C objects can be messaged using the `msg_send!` macro:
1414

1515
```rust , no_run
1616
use objc2::{class, msg_send};
17-
use objc2::runtime::{BOOL, Object};
17+
use objc2::runtime::{Bool, Object};
1818

1919
let cls = class!(NSObject);
2020
unsafe {
2121
let obj: *mut Object = msg_send![cls, new];
2222
let hash: usize = msg_send![obj, hash];
23-
let is_kind: BOOL = msg_send![obj, isKindOfClass: cls];
23+
let is_kind: Bool = msg_send![obj, isKindOfClass: cls];
2424
// Even void methods must have their return type annotated
2525
let _: () = msg_send![obj, release];
2626
}

objc2/src/bool.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,21 @@ impl fmt::Debug for Bool {
9999
}
100100
}
101101

102+
// SAFETY: `Bool` is `repr(transparent)`.
102103
unsafe impl Encode for Bool {
103104
const ENCODING: Encoding<'static> = objc2_sys::BOOL::ENCODING;
104105
}
105106

107+
// Note that we shouldn't delegate to `BOOL`'s `ENCODING_REF` since `BOOL` is
108+
// sometimes `i8`/`u8`, and their `ENCODING_REF`s are `Encoding::String`,
109+
// which is incorrect for `BOOL`:
110+
//
111+
// ```objc
112+
// @encode(BOOL); // -> "c", "C" or "B"
113+
// @encode(BOOL*); // -> "^c", "^C" or "^B"
114+
// @encode(char); // -> "c" or "C"
115+
// @encode(char*); // -> "*"
116+
// ```
106117
unsafe impl RefEncode for Bool {
107118
const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
108119
}

objc2/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ Objective-C objects can be messaged using the [`msg_send!`](macro.msg_send!.html
77
88
``` no_run
99
# use objc2::{class, msg_send};
10-
# use objc2::runtime::{BOOL, Class, Object};
10+
# use objc2::runtime::{Bool, Class, Object};
1111
# unsafe {
1212
let cls = class!(NSObject);
1313
let obj: *mut Object = msg_send![cls, new];
1414
let hash: usize = msg_send![obj, hash];
15-
let is_kind: BOOL = msg_send![obj, isKindOfClass: cls];
15+
let is_kind: Bool = msg_send![obj, isKindOfClass: cls];
1616
// Even void methods must have their return type annotated
1717
let _: () = msg_send![obj, release];
1818
# }

objc2/src/message/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,13 +165,13 @@ pub unsafe trait MessageReceiver: private::Sealed {
165165
/// # Example
166166
/// ``` no_run
167167
/// # use objc2::{class, msg_send, sel};
168-
/// # use objc2::runtime::{BOOL, Class, Object};
168+
/// # use objc2::runtime::{Bool, Class, Object};
169169
/// # use objc2::MessageReceiver;
170170
/// let obj: &Object;
171171
/// # obj = unsafe { msg_send![class!(NSObject), new] };
172172
/// let sel = sel!(isKindOfClass:);
173173
/// // Verify isKindOfClass: takes one Class and returns a BOOL
174-
/// let result = obj.verify_message::<(&Class,), BOOL>(sel);
174+
/// let result = obj.verify_message::<(&Class,), Bool>(sel);
175175
/// assert!(result.is_ok());
176176
/// ```
177177
fn verify_message<A, R>(&self, sel: Sel) -> Result<(), MessageError>

objc2_encode/src/encode.rs

Lines changed: 132 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
use core::{ffi::c_void, mem::ManuallyDrop, pin::Pin, ptr::NonNull};
1+
use core::ffi::c_void;
2+
use core::mem::ManuallyDrop;
3+
use core::num::{
4+
NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU16, NonZeroU32,
5+
NonZeroU64, NonZeroU8, NonZeroUsize, Wrapping,
6+
};
7+
use core::pin::Pin;
8+
use core::ptr::NonNull;
29

310
use crate::Encoding;
411

@@ -160,17 +167,22 @@ encode_impls!(
160167
u64 => ULongLong,
161168
f32 => Float,
162169
f64 => Double,
163-
() => Void,
164-
*mut i8 => String,
165-
*const i8 => String,
166-
*mut u8 => String,
167-
*const u8 => String,
168170
);
169171

172+
/// To allow usage as the return type of generic functions.
173+
///
174+
/// You should not rely on this encoding to exist for any other purpose (since
175+
/// `()` is not FFI-safe)!
176+
///
177+
/// TODO: Figure out a way to remove this.
178+
unsafe impl Encode for () {
179+
const ENCODING: Encoding<'static> = Encoding::Void;
180+
}
181+
170182
/// Using this directly is heavily discouraged, since the type of BOOL differs
171183
/// across platforms.
172184
///
173-
/// Use `objc2_sys::BOOL::ENCODING` or `objc2::runtime::Bool` instead.
185+
/// Use `objc2::runtime::Bool::ENCODING` instead.
174186
unsafe impl Encode for bool {
175187
const ENCODING: Encoding<'static> = Encoding::Bool;
176188
}
@@ -194,16 +206,45 @@ encode_impls_size!(
194206
usize => (u16, u32, u64),
195207
);
196208

197-
/// Simple helper for implementing [`Encode`] for integer types.
209+
/// Simple helper for implementing [`RefEncode`].
210+
macro_rules! pointer_refencode_impl {
211+
($($t:ty),*) => ($(
212+
unsafe impl RefEncode for $t {
213+
const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
214+
}
215+
)*);
216+
}
217+
218+
pointer_refencode_impl!(bool, i16, i32, i64, isize, u16, u32, u64, usize, f32, f64);
219+
220+
/// Pointers to [`i8`] use the special [`Encoding::String`] encoding.
221+
unsafe impl RefEncode for i8 {
222+
const ENCODING_REF: Encoding<'static> = Encoding::String;
223+
}
224+
225+
/// Pointers to [`u8`] use the special [`Encoding::String`] encoding.
226+
unsafe impl RefEncode for u8 {
227+
const ENCODING_REF: Encoding<'static> = Encoding::String;
228+
}
229+
230+
/// Simple helper for implementing [`Encode`] for nonzero integer types.
198231
macro_rules! encode_impls_nonzero {
199232
($($nonzero:ident => $type:ty,)*) => ($(
200-
unsafe impl Encode for core::num::$nonzero {
233+
unsafe impl Encode for $nonzero {
201234
const ENCODING: Encoding<'static> = <$type>::ENCODING;
202235
}
203236

204-
unsafe impl Encode for Option<core::num::$nonzero> {
237+
unsafe impl Encode for Option<$nonzero> {
205238
const ENCODING: Encoding<'static> = <$type>::ENCODING;
206239
}
240+
241+
unsafe impl RefEncode for $nonzero {
242+
const ENCODING_REF: Encoding<'static> = <$type>::ENCODING_REF;
243+
}
244+
245+
unsafe impl RefEncode for Option<$nonzero> {
246+
const ENCODING_REF: Encoding<'static> = <$type>::ENCODING_REF;
247+
}
207248
)*);
208249
}
209250

@@ -229,12 +270,20 @@ unsafe impl Encode for *const c_void {
229270
const ENCODING: Encoding<'static> = Encoding::Pointer(&Encoding::Void);
230271
}
231272

273+
unsafe impl RefEncode for *const c_void {
274+
const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
275+
}
276+
232277
/// [`Encode`] is implemented manually for `*mut c_void`, instead of
233278
/// implementing [`RefEncode`], to discourage creating `&mut c_void`.
234279
unsafe impl Encode for *mut c_void {
235280
const ENCODING: Encoding<'static> = Encoding::Pointer(&Encoding::Void);
236281
}
237282

283+
unsafe impl RefEncode for *mut c_void {
284+
const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
285+
}
286+
238287
unsafe impl<T: Encode, const LENGTH: usize> Encode for [T; LENGTH] {
239288
const ENCODING: Encoding<'static> = Encoding::Array(LENGTH, &T::ENCODING);
240289
}
@@ -265,6 +314,16 @@ unsafe impl<T: RefEncode> RefEncode for Pin<T> {
265314
const ENCODING_REF: Encoding<'static> = T::ENCODING_REF;
266315
}
267316

317+
// SAFETY: `Wrapping` is `repr(transparent)`.
318+
unsafe impl<T: Encode> Encode for Wrapping<T> {
319+
const ENCODING: Encoding<'static> = T::ENCODING;
320+
}
321+
322+
// SAFETY: `Wrapping` is `repr(transparent)`.
323+
unsafe impl<T: RefEncode> RefEncode for Wrapping<T> {
324+
const ENCODING_REF: Encoding<'static> = T::ENCODING_REF;
325+
}
326+
268327
/// Helper for implementing `Encode`/`RefEncode` for pointers to types that
269328
/// implement `RefEncode`.
270329
///
@@ -423,3 +482,66 @@ encode_args_impl!(A, B, C, D, E, F, G, H, I);
423482
encode_args_impl!(A, B, C, D, E, F, G, H, I, J);
424483
encode_args_impl!(A, B, C, D, E, F, G, H, I, J, K);
425484
encode_args_impl!(A, B, C, D, E, F, G, H, I, J, K, L);
485+
486+
#[cfg(test)]
487+
mod tests {
488+
use super::*;
489+
490+
#[test]
491+
fn test_c_string() {
492+
assert_eq!(i8::ENCODING, Encoding::Char);
493+
assert_eq!(u8::ENCODING, Encoding::UChar);
494+
495+
assert_eq!(<*const i8>::ENCODING, Encoding::String);
496+
assert_eq!(<&u8>::ENCODING, Encoding::String);
497+
assert_eq!(i8::ENCODING_REF, Encoding::String);
498+
assert_eq!(i8::ENCODING_REF, Encoding::String);
499+
500+
assert_eq!(
501+
<*const *const i8>::ENCODING,
502+
Encoding::Pointer(&Encoding::String)
503+
);
504+
assert_eq!(<&&u8>::ENCODING, Encoding::Pointer(&Encoding::String));
505+
}
506+
507+
#[test]
508+
fn test_i32() {
509+
assert_eq!(i32::ENCODING, Encoding::Int);
510+
assert_eq!(<&i32>::ENCODING, Encoding::Pointer(&Encoding::Int));
511+
assert_eq!(
512+
<&&i32>::ENCODING,
513+
Encoding::Pointer(&Encoding::Pointer(&Encoding::Int))
514+
);
515+
}
516+
517+
#[test]
518+
fn test_void() {
519+
// TODO: Remove this
520+
assert_eq!(<()>::ENCODING, Encoding::Void);
521+
assert_eq!(
522+
<*const c_void>::ENCODING,
523+
Encoding::Pointer(&Encoding::Void)
524+
);
525+
assert_eq!(
526+
<&*const c_void>::ENCODING,
527+
Encoding::Pointer(&Encoding::Pointer(&Encoding::Void))
528+
);
529+
530+
// Shouldn't compile
531+
// assert_eq!(<c_void>::ENCODING, Encoding::Void);
532+
// assert_eq!(<*const ()>::ENCODING, Encoding::Pointer(&Encoding::Void));
533+
// assert_eq!(<&c_void>::ENCODING, Encoding::Pointer(&Encoding::Void));
534+
}
535+
536+
#[test]
537+
fn test_extern_fn_pointer() {
538+
assert_eq!(
539+
<extern "C" fn()>::ENCODING,
540+
Encoding::Pointer(&Encoding::Unknown)
541+
);
542+
assert_eq!(
543+
<extern "C" fn(x: ()) -> ()>::ENCODING,
544+
Encoding::Pointer(&Encoding::Unknown)
545+
);
546+
}
547+
}

objc2_sys/src/types.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ mod inner {
6565
///
6666
/// The type of this varies across platforms, so to convert an it into a Rust
6767
/// [`bool`], always compare it with [`YES`][`crate::YES`] or [`NO`][`crate::NO`].
68+
///
69+
/// Note that this type implements `objc2_encode::Encode` and
70+
/// `objc2_encode::RefEncode`, but the `RefEncode` implementation is wrong
71+
/// on some platforms! You should only use this on FFI boundaries, otherwise
72+
/// prefer `objc2::runtime::Bool`.
6873
pub type BOOL = inner::BOOL;
6974

7075
/// An immutable pointer to a selector.

0 commit comments

Comments
 (0)