diff --git a/objc2/CHANGELOG.md b/objc2/CHANGELOG.md index d5a75ebe8..00eca8146 100644 --- a/objc2/CHANGELOG.md +++ b/objc2/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added * Allow directly specifying class name in `extern_class!` macro. +* Added `extern_protocol!` macro and `ProtocolType` trait. ### Changed * Allow other types than `&Class` as the receiver in `msg_send_id!` methods diff --git a/objc2/src/foundation/object.rs b/objc2/src/foundation/object.rs index a7fcd2563..031e5937b 100644 --- a/objc2/src/foundation/object.rs +++ b/objc2/src/foundation/object.rs @@ -3,8 +3,8 @@ use core::hash; use super::NSString; use crate::rc::{DefaultId, Id, Owned, Shared}; -use crate::runtime::{Class, Object}; -use crate::{ClassType, __inner_extern_class, class, extern_methods, msg_send_id}; +use crate::runtime::{Class, Object, Protocol}; +use crate::{ClassType, ProtocolType, __inner_extern_class, class, extern_methods, msg_send_id}; __inner_extern_class! { @__inner @@ -33,6 +33,23 @@ unsafe impl ClassType for NSObject { } } +unsafe impl ProtocolType for NSObject { + type Super = Object; + const NAME: &'static str = "NSObject"; + + fn protocol() -> &'static Protocol { + Protocol::get(::NAME).expect("Could not find NSObject protocol") + } + + fn as_super(&self) -> &Self::Super { + &self.__inner + } + + fn as_super_mut(&mut self) -> &mut Self::Super { + &mut self.__inner + } +} + extern_methods!( unsafe impl NSObject { pub fn new() -> Id { diff --git a/objc2/src/lib.rs b/objc2/src/lib.rs index e8d069954..5bf2c116e 100644 --- a/objc2/src/lib.rs +++ b/objc2/src/lib.rs @@ -199,6 +199,7 @@ pub use objc2_encode::{Encode, EncodeArguments, Encoding, RefEncode}; pub use crate::class_type::ClassType; pub use crate::message::{Message, MessageArguments, MessageReceiver}; +pub use crate::protocol_type::ProtocolType; #[cfg(feature = "malloc")] pub use crate::verify::VerificationError; @@ -224,6 +225,7 @@ pub mod exception; pub mod foundation; mod macros; mod message; +mod protocol_type; pub mod rc; pub mod runtime; #[cfg(test)] diff --git a/objc2/src/macros.rs b/objc2/src/macros.rs index b3b1cd947..868979cb8 100644 --- a/objc2/src/macros.rs +++ b/objc2/src/macros.rs @@ -2,6 +2,7 @@ mod __rewrite_self_arg; mod declare_class; mod extern_class; mod extern_methods; +mod extern_protocol; /// Gets a reference to a [`Class`] from the given name. /// diff --git a/objc2/src/macros/declare_class.rs b/objc2/src/macros/declare_class.rs index 5934ea307..3352c96dd 100644 --- a/objc2/src/macros/declare_class.rs +++ b/objc2/src/macros/declare_class.rs @@ -626,10 +626,10 @@ macro_rules! declare_class { REGISTER_CLASS.call_once(|| { let superclass = <$superclass as $crate::ClassType>::class(); - let mut builder = $crate::declare::ClassBuilder::new(Self::NAME, superclass).unwrap_or_else(|| { + let mut builder = $crate::declare::ClassBuilder::new(::NAME, superclass).unwrap_or_else(|| { $crate::__macro_helpers::panic!( "could not create new class {}. Perhaps a class with that name already exists?", - Self::NAME, + ::NAME, ) }); @@ -660,7 +660,7 @@ macro_rules! declare_class { }); // We just registered the class, so it should be available - $crate::runtime::Class::get(Self::NAME).unwrap() + $crate::runtime::Class::get(::NAME).unwrap() } #[inline] diff --git a/objc2/src/macros/extern_protocol.rs b/objc2/src/macros/extern_protocol.rs new file mode 100644 index 000000000..d54c32819 --- /dev/null +++ b/objc2/src/macros/extern_protocol.rs @@ -0,0 +1,59 @@ +/// TODO +#[doc(alias = "@protocol")] +#[macro_export] +macro_rules! extern_protocol { + ( + $(#[$m:meta])* + $v:vis struct $name:ident; + + unsafe impl ProtocolType for $for:ty { + $(#[inherits($($inheritance_rest:ty),+)])? + type Super = $superprotocol:ty; + + $(const NAME: &'static str = $name_const:literal;)? + } + ) => { + $crate::__inner_extern_class!( + @__inner + + $(#[$m])* + $v struct ($name) {} + + unsafe impl () for $for { + INHERITS = [$superprotocol, $($($inheritance_rest,)+)? $crate::runtime::Object]; + } + ); + + unsafe impl ProtocolType for $for { + type Super = $superprotocol; + const NAME: &'static str = $crate::__select_name!($name; $($name_const)?); + + #[inline] + fn protocol() -> &'static $crate::runtime::Protocol { + $crate::runtime::Protocol::get(::NAME).unwrap_or_else(|| { + $crate::__macro_helpers::panic!("could not find protocol {}", Self::NAME) + }) + } + + #[inline] + fn as_super(&self) -> &Self::Super { + &self.__inner + } + + #[inline] + fn as_super_mut(&mut self) -> &mut Self::Super { + &mut self.__inner + } + } + + const _: () = { + if $crate::__macro_helpers::size_of::<$name>() != 0 { + panic!(concat!( + "the struct ", + stringify!($name), + " is not zero-sized!", + )) + } + }; + }; +} diff --git a/objc2/src/protocol_type.rs b/objc2/src/protocol_type.rs new file mode 100644 index 000000000..405641125 --- /dev/null +++ b/objc2/src/protocol_type.rs @@ -0,0 +1,38 @@ +use crate::runtime::Protocol; +use crate::Message; + +/// Marks types that represent specific protocols. +/// +/// TODO. +/// +/// +/// # Safety +/// +/// Same as [`ClassType`], TODO. +/// +/// [`ClassType`]: crate::ClassType +pub unsafe trait ProtocolType: Message { + /// The protocol that this protocol inherits. + type Super: Message; + + /// The name of the Objective-C protocol that this type represents. + const NAME: &'static str; + + /// Get a reference to the Objective-C protocol that this type represents. + /// + /// May register the protocol with the runtime if it wasn't already. + /// + /// + /// # Panics + /// + /// This may panic if something went wrong with getting or declaring the + /// protocol, e.g. if the program is not properly linked to the framework + /// that defines the protocol. + fn protocol() -> &'static Protocol; + + /// Get an immutable reference to the superprotocol. + fn as_super(&self) -> &Self::Super; + + /// Get a mutable reference to the superprotocol. + fn as_super_mut(&mut self) -> &mut Self::Super; +} diff --git a/objc2/src/rc/id.rs b/objc2/src/rc/id.rs index 1f7e5155e..e3bd256e9 100644 --- a/objc2/src/rc/id.rs +++ b/objc2/src/rc/id.rs @@ -9,7 +9,7 @@ use super::Allocated; use super::AutoreleasePool; use super::{Owned, Ownership, Shared}; use crate::ffi; -use crate::{ClassType, Message}; +use crate::{ClassType, Message, ProtocolType}; /// An pointer for Objective-C reference counted objects. /// @@ -650,6 +650,18 @@ where } } +impl Id +where + T::Super: 'static, +{ + /// Convert the object into it's superprotocol. + #[inline] + pub fn into_super_protocol(this: Self) -> Id { + // SAFETY: Similar to `Id::into_super` + unsafe { Self::cast::(this) } + } +} + impl From> for Id { /// Convert an owned to a shared [`Id`], allowing it to be cloned. ///