From aa12984f126fa67e63c9a4de402967609ee12db6 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sat, 9 Jul 2022 23:01:06 +0200 Subject: [PATCH] Change protocol implementation syntax This allows using types in the inheritance chain again --- objc2-foundation/examples/declaration.rs | 11 ++-- objc2-foundation/src/macros.rs | 77 ++++++++++++++++++++---- objc2-foundation/src/zone.rs | 3 +- 3 files changed, 73 insertions(+), 18 deletions(-) diff --git a/objc2-foundation/examples/declaration.rs b/objc2-foundation/examples/declaration.rs index 3f555d5a5..60e96307a 100644 --- a/objc2-foundation/examples/declaration.rs +++ b/objc2-foundation/examples/declaration.rs @@ -14,11 +14,6 @@ extern_class! { } declare_class! { - // For some reason, `NSApplicationDelegate` is not a "real" protocol we - // can retrieve using `objc_getProtocol` - it seems it is created by - // `clang` only when used in Objective-C... - // - // TODO: Investigate this! unsafe struct CustomAppDelegate: NSResponder, NSObject { pub ivar: u8, another_ivar: Bool, @@ -57,6 +52,12 @@ declare_class! { println!("A class method!"); } } + + // For some reason, `NSApplicationDelegate` is not a "real" protocol we + // can retrieve using `objc_getProtocol` - it seems it is created by + // `clang` only when used in Objective-C... + // + // TODO: Investigate this! } impl CustomAppDelegate { diff --git a/objc2-foundation/src/macros.rs b/objc2-foundation/src/macros.rs index 1c805393f..479f69f40 100644 --- a/objc2-foundation/src/macros.rs +++ b/objc2-foundation/src/macros.rs @@ -752,15 +752,15 @@ macro_rules! __inner_declare_class { /// /// # Examples /// -/// Declare a class `MyCustomObject` with a few instance variables and -/// methods. +/// Declare a class `MyCustomObject` that inherits `NSObject`, has a few +/// instance variables and methods, and implements the `NSCopying` protocol. /// /// ``` /// use std::os::raw::c_int; /// use objc2::{msg_send, msg_send_bool, msg_send_id}; /// use objc2::rc::{Id, Owned}; /// use objc2::runtime::Bool; -/// use objc2_foundation::{declare_class, NSObject}; +/// use objc2_foundation::{declare_class, NSCopying, NSObject, NSZone}; /// # /// # #[cfg(feature = "gnustep-1-7")] /// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() }; @@ -797,6 +797,15 @@ macro_rules! __inner_declare_class { /// Bool::YES /// } /// } +/// +/// unsafe impl protocol NSCopying { +/// @sel(copyWithZone:) +/// fn copy_with_zone(&self, _zone: *const NSZone) -> *mut Self { +/// let mut obj = Self::new(*self.foo); +/// *obj.bar = *self.bar; +/// obj.autorelease_return() +/// } +/// } /// } /// /// impl MyCustomObject { @@ -814,11 +823,19 @@ macro_rules! __inner_declare_class { /// } /// } /// +/// unsafe impl NSCopying for MyCustomObject { +/// type Ownership = Owned; +/// type Output = Self; +/// } +/// /// fn main() { /// let obj = MyCustomObject::new(3); /// assert_eq!(*obj.foo, 3); /// assert_eq!(*obj.bar, 42); +/// +/// let obj = obj.copy(); /// assert_eq!(obj.get_foo(), 3); +/// /// assert!(MyCustomObject::my_class_method()); /// } /// ``` @@ -828,7 +845,7 @@ macro_rules! __inner_declare_class { /// ```text /// #import /// -/// @interface MyCustomObject: NSObject { +/// @interface MyCustomObject: NSObject { /// int bar; /// } /// @@ -861,13 +878,19 @@ macro_rules! __inner_declare_class { /// return YES; /// } /// +/// - (id)copyWithZone:(NSZone *)_zone { +/// MyCustomObject* obj = [[MyCustomObject alloc] initWithFoo: self->foo]; +/// obj->bar = self->bar; +/// return obj; +/// } +/// /// @end /// ``` #[macro_export] macro_rules! declare_class { { $(#[$m:meta])* - unsafe $v:vis struct $name:ident: $inherits:ident $(, $inheritance_rest:ident)* $(<$($protocols:ident),+ $(,)?>)? { + unsafe $v:vis struct $name:ident: $inherits:ty $(, $inheritance_rest:ty)* { $($ivar_v:vis $ivar:ident: $ivar_ty:ty,)* } @@ -875,6 +898,13 @@ macro_rules! declare_class { unsafe impl { $($methods:tt)* } + + $( + $(#[$impl_protocol_m:meta])* + unsafe impl protocol $protocols:ident { + $($protocol_methods:tt)* + } + ),* } => { $( #[allow(non_camel_case_types)] @@ -916,13 +946,6 @@ macro_rules! declare_class { let superclass = <$inherits>::class(); let mut builder = ClassBuilder::new(stringify!($name), superclass).unwrap(); - // Implement protocols - $( - $( - builder.add_protocol(Protocol::get(stringify!($protocols)).unwrap()); - )+ - )? - $( builder.add_ivar::<<$ivar as $crate::objc2::declare::IvarType>::Type>( <$ivar as $crate::objc2::declare::IvarType>::NAME @@ -940,6 +963,22 @@ macro_rules! declare_class { } } + // Implement protocols + $( + builder.add_protocol(Protocol::get(stringify!($protocols)).unwrap()); + + // SAFETY: Upheld by caller + unsafe { + $crate::__inner_declare_class! { + @rewrite_methods + @register_out + @builder + + $($protocol_methods)* + } + } + )* + let _cls = builder.register(); }); @@ -958,6 +997,20 @@ macro_rules! declare_class { $($methods)* } } + + // Protocol methods + $( + $(#[$impl_protocol_m])* + impl $name { + $crate::__inner_declare_class! { + @rewrite_methods + @method_out + @__builder + + $($protocol_methods)* + } + } + )* }; } diff --git a/objc2-foundation/src/zone.rs b/objc2-foundation/src/zone.rs index ad478c815..465c1a395 100644 --- a/objc2-foundation/src/zone.rs +++ b/objc2-foundation/src/zone.rs @@ -5,7 +5,8 @@ use objc2::{Encoding, RefEncode}; /// A type used to identify and manage memory zones. /// /// Zones are ignored on all newer platforms, you should very rarely need to -/// use this. +/// use this, but may be useful if you need to implement `copyWithZone:` or +/// `allocWithZone:`. /// /// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nszone?language=objc). #[derive(Debug)]