Skip to content

Commit 20eb41c

Browse files
authored
Merge pull request #153 from madsmtm/better-declaration
Slightly better class declaration
2 parents d3dc9e5 + 92f85da commit 20eb41c

15 files changed

+245
-78
lines changed

objc2-encode/src/encode.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,8 +430,7 @@ encode_pointer_impls!(
430430
///
431431
/// Ideally we'd implement it for all function pointers, but due to coherence
432432
/// issues, see <https://github.com/rust-lang/rust/issues/56105>, function
433-
/// pointers that take arguments with "special lifetimes" (don't know the
434-
/// termonology) don't get implemented properly.
433+
/// pointers that are higher-ranked over lifetimes don't get implemented.
435434
///
436435
/// We could fix it by adding those impls and allowing `coherence_leak_check`,
437436
/// but it would have to be done for _all_ references, `Option<&T>` and such as

objc2-foundation/examples/class_with_lifetime.rs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
#![deny(unsafe_op_in_unsafe_fn)]
12
use std::marker::PhantomData;
23
use std::sync::Once;
34

4-
use objc2::declare::ClassDecl;
5+
use objc2::declare::ClassBuilder;
56
use objc2::rc::{Id, Owned, Shared};
67
use objc2::runtime::{Class, Object, Sel};
78
use objc2::{msg_send, sel};
@@ -47,23 +48,30 @@ impl<'a> MyObject<'a> {
4748
fn class() -> &'static Class {
4849
MYOBJECT_REGISTER_CLASS.call_once(|| {
4950
let superclass = NSObject::class();
50-
let mut decl = ClassDecl::new("MyObject", superclass).unwrap();
51-
decl.add_ivar::<Option<&mut u8>>("_number_ptr");
52-
53-
extern "C" fn init_with_ptr(this: &mut Object, _cmd: Sel, ptr: *mut u8) -> *mut Object {
54-
unsafe {
55-
this.set_ivar("_number_ptr", ptr);
51+
let mut builder = ClassBuilder::new("MyObject", superclass).unwrap();
52+
builder.add_ivar::<Option<&mut u8>>("_number_ptr");
53+
54+
unsafe extern "C" fn init_with_ptr(
55+
this: *mut Object,
56+
_cmd: Sel,
57+
ptr: *mut u8,
58+
) -> *mut Object {
59+
let this: *mut Object = unsafe { msg_send![super(this, NSObject::class()), init] };
60+
if let Some(this) = unsafe { this.as_mut() } {
61+
unsafe {
62+
this.set_ivar("_number_ptr", ptr);
63+
}
5664
}
5765
this
5866
}
5967

6068
unsafe {
61-
let init_with_ptr: extern "C" fn(&mut Object, Sel, *mut u8) -> *mut Object =
69+
let init_with_ptr: unsafe extern "C" fn(*mut Object, Sel, *mut u8) -> *mut Object =
6270
init_with_ptr;
63-
decl.add_method(sel!(initWithPtr:), init_with_ptr);
71+
builder.add_method(sel!(initWithPtr:), init_with_ptr);
6472
}
6573

66-
decl.register();
74+
builder.register();
6775
});
6876

6977
Class::get("MyObject").unwrap()

objc2-foundation/examples/custom_class.rs

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::sync::Once;
22

3-
use objc2::declare::ClassDecl;
3+
use objc2::declare::ClassBuilder;
44
use objc2::rc::{Id, Owned};
55
use objc2::runtime::{Class, Object, Sel};
66
use objc2::{msg_send, sel};
@@ -39,28 +39,26 @@ impl MYObject {
3939
fn class() -> &'static Class {
4040
MYOBJECT_REGISTER_CLASS.call_once(|| {
4141
let superclass = NSObject::class();
42-
let mut decl = ClassDecl::new("MYObject", superclass).unwrap();
43-
decl.add_ivar::<u32>("_number");
42+
let mut builder = ClassBuilder::new("MYObject", superclass).unwrap();
43+
builder.add_ivar::<u32>("_number");
4444

4545
// Add ObjC methods for getting and setting the number
46-
extern "C" fn my_object_set_number(this: &mut Object, _cmd: Sel, number: u32) {
47-
unsafe {
48-
this.set_ivar("_number", number);
49-
}
46+
extern "C" fn my_object_set_number(this: &mut MYObject, _cmd: Sel, number: u32) {
47+
this.set_number(number);
5048
}
5149

52-
extern "C" fn my_object_get_number(this: &Object, _cmd: Sel) -> u32 {
53-
unsafe { *this.ivar("_number") }
50+
extern "C" fn my_object_get_number(this: &MYObject, _cmd: Sel) -> u32 {
51+
this.number()
5452
}
5553

5654
unsafe {
57-
let set_number: extern "C" fn(&mut Object, Sel, u32) = my_object_set_number;
58-
decl.add_method(sel!(setNumber:), set_number);
59-
let get_number: extern "C" fn(&Object, Sel) -> u32 = my_object_get_number;
60-
decl.add_method(sel!(number), get_number);
55+
let set_number: extern "C" fn(&mut MYObject, Sel, u32) = my_object_set_number;
56+
builder.add_method(sel!(setNumber:), set_number);
57+
let get_number: extern "C" fn(&MYObject, Sel) -> u32 = my_object_get_number;
58+
builder.add_method(sel!(number), get_number);
6159
}
6260

63-
decl.register();
61+
builder.register();
6462
});
6563

6664
Class::get("MYObject").unwrap()

objc2-foundation/src/array.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,10 @@ impl<T: Message, O: Ownership> NSMutableArray<T, O> {
303303
NSComparisonResult::from((*closure)(obj1, obj2))
304304
}
305305

306-
let f: extern "C" fn(_, _, _) -> _ = compare_with_closure::<T, F>;
306+
// We can't name the actual lifetimes in use here, so use `_`.
307+
// See also https://github.com/rust-lang/rust/issues/56105
308+
let f: extern "C" fn(_, _, *mut c_void) -> NSComparisonResult =
309+
compare_with_closure::<T, F>;
307310

308311
// Grab a type-erased pointer to the closure (a pointer to stack).
309312
let mut closure = compare;

objc2/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1818
* Consistently allow trailing commas in `msg_send!`.
1919
* Added `msg_send_bool!`, a less error-prone version of `msg_send!` for
2020
Objective-C methods that return `BOOL`.
21+
* Implemented `MethodImplementation` for `unsafe` function pointers.
2122

2223
### Changed
2324
* **BREAKING**: Changed signature of `Id::new` and `Id::retain` from
@@ -33,6 +34,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
3334
let obj: *mut Object = unsafe { msg_send![class!(NSObject), new] };
3435
let obj = unsafe { Id::new(obj) }.expect("Failed to allocate object.");
3536
```
37+
* Allow specifying any receiver `T: Message` for methods added with
38+
`ClassBuilder::add_method`.
39+
* Renamed `ClassDecl` and `ProtocolDecl` to `ClassBuilder` and
40+
`ProtocolBuilder`. The old names are kept as deprecated aliases.
3641

3742
### Fixed
3843
* Properly sealed the `MessageArguments` trait (it already had a hidden

objc2/src/declare.rs

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Functionality for declaring Objective-C classes.
22
//!
3-
//! Classes can be declared using the [`ClassDecl`] struct. Instance variables
3+
//! Classes can be declared using the [`ClassBuilder`] struct. Instance variables
44
//! and methods can then be added before the class is ultimately registered.
55
//!
66
//! # Example
@@ -11,11 +11,11 @@
1111
//!
1212
//! ```no_run
1313
//! use objc2::{class, sel};
14-
//! use objc2::declare::ClassDecl;
14+
//! use objc2::declare::ClassBuilder;
1515
//! use objc2::runtime::{Class, Object, Sel};
1616
//!
1717
//! let superclass = class!(NSObject);
18-
//! let mut decl = ClassDecl::new("MyNumber", superclass).unwrap();
18+
//! let mut decl = ClassBuilder::new("MyNumber", superclass).unwrap();
1919
//!
2020
//! // Add an instance variable
2121
//! decl.add_ivar::<u32>("_number");
@@ -78,6 +78,10 @@ macro_rules! method_decl_impl {
7878
($($t:ident),*) => (
7979
method_decl_impl!(-T, R, extern "C" fn(&T, Sel $(, $t)*) -> R, $($t),*);
8080
method_decl_impl!(-T, R, extern "C" fn(&mut T, Sel $(, $t)*) -> R, $($t),*);
81+
method_decl_impl!(-T, R, unsafe extern "C" fn(*const T, Sel $(, $t)*) -> R, $($t),*);
82+
method_decl_impl!(-T, R, unsafe extern "C" fn(*mut T, Sel $(, $t)*) -> R, $($t),*);
83+
method_decl_impl!(-T, R, unsafe extern "C" fn(&T, Sel $(, $t)*) -> R, $($t),*);
84+
method_decl_impl!(-T, R, unsafe extern "C" fn(&mut T, Sel $(, $t)*) -> R, $($t),*);
8185
);
8286
}
8387

@@ -120,10 +124,14 @@ fn log2_align_of<T>() -> u8 {
120124
/// A type for declaring a new class and adding new methods and ivars to it
121125
/// before registering it.
122126
#[derive(Debug)]
123-
pub struct ClassDecl {
127+
pub struct ClassBuilder {
124128
cls: NonNull<Class>,
125129
}
126130

131+
#[doc(hidden)]
132+
#[deprecated = "Use `ClassBuilder` instead."]
133+
pub type ClassDecl = ClassBuilder;
134+
127135
// SAFETY: The stuff that touch global state does so using locks internally.
128136
//
129137
// Modifying the class itself can only be done through `&mut`, so Sync is
@@ -134,11 +142,11 @@ pub struct ClassDecl {
134142
// when doing so...).
135143
//
136144
// Finally, there are no requirements that the class must be registered on the
137-
// same thread that allocated it.
138-
unsafe impl Send for ClassDecl {}
139-
unsafe impl Sync for ClassDecl {}
145+
// same thread that allocated it (so Send is safe).
146+
unsafe impl Send for ClassBuilder {}
147+
unsafe impl Sync for ClassBuilder {}
140148

141-
impl ClassDecl {
149+
impl ClassBuilder {
142150
fn as_ptr(&self) -> *mut ffi::objc_class {
143151
self.cls.as_ptr().cast()
144152
}
@@ -150,16 +158,16 @@ impl ClassDecl {
150158
NonNull::new(cls.cast()).map(|cls| Self { cls })
151159
}
152160

153-
/// Constructs a [`ClassDecl`] with the given name and superclass.
161+
/// Constructs a [`ClassBuilder`] with the given name and superclass.
154162
///
155163
/// Returns [`None`] if the class couldn't be allocated, or a class with
156164
/// that name already exist.
157165
pub fn new(name: &str, superclass: &Class) -> Option<Self> {
158166
Self::with_superclass(name, Some(superclass))
159167
}
160168

161-
/// Constructs a [`ClassDecl`] declaring a new root class with the given
162-
/// name.
169+
/// Constructs a [`ClassBuilder`] declaring a new root class with the
170+
/// given name.
163171
///
164172
/// Returns [`None`] if the class couldn't be allocated.
165173
///
@@ -173,11 +181,10 @@ impl ClassDecl {
173181
/// Functionality it expects, like implementations of `-retain` and
174182
/// `-release` used by ARC, will not be present otherwise.
175183
pub fn root(name: &str, intitialize_fn: extern "C" fn(&Class, Sel)) -> Option<Self> {
176-
let mut decl = Self::with_superclass(name, None);
177-
if let Some(ref mut decl) = decl {
178-
unsafe { decl.add_class_method(sel!(initialize), intitialize_fn) };
179-
}
180-
decl
184+
Self::with_superclass(name, None).map(|mut this| {
185+
unsafe { this.add_class_method(sel!(initialize), intitialize_fn) };
186+
this
187+
})
181188
}
182189

183190
/// Adds a method with the given name and implementation.
@@ -191,9 +198,10 @@ impl ClassDecl {
191198
///
192199
/// The caller must ensure that the types match those that are expected
193200
/// when the method is invoked from Objective-C.
194-
pub unsafe fn add_method<F>(&mut self, sel: Sel, func: F)
201+
pub unsafe fn add_method<T, F>(&mut self, sel: Sel, func: F)
195202
where
196-
F: MethodImplementation<Callee = Object>,
203+
T: Message + ?Sized, // TODO: Disallow `Class`
204+
F: MethodImplementation<Callee = T>,
197205
{
198206
let encs = F::Args::ENCODINGS;
199207
let sel_args = count_args(sel);
@@ -290,8 +298,8 @@ impl ClassDecl {
290298

291299
// fn add_property(&self, name: &str, attributes: &[ffi::objc_property_attribute_t]);
292300

293-
/// Registers the [`ClassDecl`], consuming it, and returns a reference to
294-
/// the newly registered [`Class`].
301+
/// Registers the [`ClassBuilder`], consuming it, and returns a reference
302+
/// to the newly registered [`Class`].
295303
pub fn register(self) -> &'static Class {
296304
// Forget self, otherwise the class will be disposed in drop
297305
let cls = ManuallyDrop::new(self).cls;
@@ -300,7 +308,7 @@ impl ClassDecl {
300308
}
301309
}
302310

303-
impl Drop for ClassDecl {
311+
impl Drop for ClassBuilder {
304312
fn drop(&mut self) {
305313
unsafe { ffi::objc_disposeClassPair(self.as_ptr()) }
306314
}
@@ -309,20 +317,24 @@ impl Drop for ClassDecl {
309317
/// A type for declaring a new protocol and adding new methods to it
310318
/// before registering it.
311319
#[derive(Debug)]
312-
pub struct ProtocolDecl {
320+
pub struct ProtocolBuilder {
313321
proto: NonNull<Protocol>,
314322
}
315323

316-
// SAFETY: Similar to ClassDecl
317-
unsafe impl Send for ProtocolDecl {}
318-
unsafe impl Sync for ProtocolDecl {}
324+
#[doc(hidden)]
325+
#[deprecated = "Use `ProtocolBuilder` instead."]
326+
pub type ProtocolDecl = ProtocolBuilder;
327+
328+
// SAFETY: Similar to ClassBuilder
329+
unsafe impl Send for ProtocolBuilder {}
330+
unsafe impl Sync for ProtocolBuilder {}
319331

320-
impl ProtocolDecl {
332+
impl ProtocolBuilder {
321333
fn as_ptr(&self) -> *mut ffi::objc_protocol {
322334
self.proto.as_ptr().cast()
323335
}
324336

325-
/// Constructs a [`ProtocolDecl`] with the given name.
337+
/// Constructs a [`ProtocolBuilder`] with the given name.
326338
///
327339
/// Returns [`None`] if the protocol couldn't be allocated.
328340
pub fn new(name: &str) -> Option<Self> {
@@ -386,7 +398,7 @@ impl ProtocolDecl {
386398
}
387399
}
388400

389-
/// Registers the [`ProtocolDecl`], consuming it and returning a reference
401+
/// Registers the [`ProtocolBuilder`], consuming it and returning a reference
390402
/// to the newly registered [`Protocol`].
391403
pub fn register(self) -> &'static Protocol {
392404
unsafe {

0 commit comments

Comments
 (0)