Skip to content

Commit

Permalink
Change protocol implementation syntax
Browse files Browse the repository at this point in the history
This allows using types in the inheritance chain again
  • Loading branch information
madsmtm committed Jul 9, 2022
1 parent 1b52e65 commit aa12984
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 18 deletions.
11 changes: 6 additions & 5 deletions objc2-foundation/examples/declaration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down
77 changes: 65 additions & 12 deletions objc2-foundation/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() };
Expand Down Expand Up @@ -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 {
Expand All @@ -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());
/// }
/// ```
Expand All @@ -828,7 +845,7 @@ macro_rules! __inner_declare_class {
/// ```text
/// #import <Foundation/Foundation.h>
///
/// @interface MyCustomObject: NSObject {
/// @interface MyCustomObject: NSObject <NSCopying> {
/// int bar;
/// }
///
Expand Down Expand Up @@ -861,20 +878,33 @@ 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,)*
}

$(#[$impl_m:meta])*
unsafe impl {
$($methods:tt)*
}

$(
$(#[$impl_protocol_m:meta])*
unsafe impl protocol $protocols:ident {
$($protocol_methods:tt)*
}
),*
} => {
$(
#[allow(non_camel_case_types)]
Expand Down Expand Up @@ -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
Expand All @@ -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();
});

Expand All @@ -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)*
}
}
)*
};
}

Expand Down
3 changes: 2 additions & 1 deletion objc2-foundation/src/zone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down

0 comments on commit aa12984

Please sign in to comment.