objc2/macros/extern_methods.rs
1/// Define methods on an external class.
2///
3/// This is a convenience macro to generate associated functions and methods
4/// that delegate to [`msg_send!`].
5///
6/// [`msg_send!`]: crate::msg_send
7///
8///
9/// # Specification
10///
11/// Within the `impl` block you can define two types of functions without
12/// bodies; ["associated functions"] and ["methods"]. These are then mapped to
13/// the Objective-C equivalents "class methods" and "instance methods", and an
14/// appropriate body is created for you. In particular, if you use `self` or
15/// the special name `this` (or `_this`), your method will assumed to be an
16/// instance method, and if you don't it will be assumed to be a class method.
17///
18/// If you specify a function/method with a body, the macro will output it
19/// unchanged.
20///
21/// The name of the function will be used for the resulting function that the
22/// user will use to access the functionality, but is otherwise not used by
23/// the macro.
24///
25/// If you use `objc2::MainThreadMarker` as a parameter type, the macro will
26/// ignore it, allowing you to neatly specify "this method must be run on the
27/// main thread". Note that due to type-system limitations, this is currently
28/// a textual match on `MainThreadMarker`; so you must use that exact
29/// identifier.
30///
31/// ["associated functions"]: https://doc.rust-lang.org/reference/items/associated-items.html#methods
32/// ["methods"]: https://doc.rust-lang.org/reference/items/associated-items.html#methods
33///
34///
35/// ## Attributes
36///
37/// You can add most normal attributes to the methods, including
38/// `#[cfg(...)]`, `#[allow(...)]`, `#[deprecated = ...]` and doc comments.
39///
40/// Exceptions and special attributes are noted below.
41///
42///
43/// ### `#[unsafe(method(...))]` (required)
44///
45/// Specify the desired selector using this attribute.
46///
47/// If the selector ends with "_", as in `#[unsafe(method(my:error:_))]`, the
48/// method is assumed to take an implicit `NSError**` parameter, which is
49/// automatically converted to a [`Result`]. See the error section in
50/// [`msg_send!`] for details.
51///
52///
53/// ### `#[unsafe(method_family = ...)]` (optional)
54///
55/// The Cocoa memory management convention is figured out automatically based
56/// on the name of the selector, but it can be overwritten with this `unsafe`
57/// attribute.
58///
59/// This is commonly done in framework crates to improve compile-time
60/// performance, as the logic to determine the family automatically can be
61/// quite taxing at scale. That said, you should rarely need to use this
62/// yourself.
63///
64/// The valid family names are:
65/// - `alloc`.
66/// - `new`.
67/// - `init`.
68/// - `copy`.
69/// - `mutableCopy`.
70///
71/// As well as the special `none` family that opts-out of being in a family.
72///
73/// This corresponds to the `__attribute__((objc_method_family(family)))` C
74/// attribute, see [Clang's documentation][clang-method-families].
75///
76/// [clang-method-families]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html#method-families
77///
78///
79/// #### Safety
80///
81/// You must ensure that the specified method family is correct.
82///
83///
84/// ### `#[cfg_attr(..., ...)]`
85///
86/// This is only supported for attributes that apply to the method itself
87/// (i.e. not supported for attributes that apply to any of the custom
88/// attributes, due to implementation difficulty).
89///
90///
91/// # Safety
92///
93/// You must ensure that any methods you declare with the
94/// `#[unsafe(method(...))]` attribute upholds the safety guarantees described
95/// in the [`msg_send!`] macro, _or_ are marked `unsafe`.
96///
97///
98/// # Examples
99///
100/// Let's create a quick custom class:
101///
102/// ```
103/// use objc2::encode::{Encode, Encoding};
104/// use objc2::ffi::NSUInteger;
105/// use objc2::rc::{Allocated, Retained};
106/// use objc2::runtime::NSObject;
107/// use objc2::{define_class, extern_methods};
108///
109/// // Shim
110/// type NSError = NSObject;
111///
112/// define_class!(
113/// // SAFETY:
114/// // - The superclass NSObject does not have any subclassing requirements.
115/// // - `MyObject` does not implement `Drop`.
116/// #[unsafe(super(NSObject))]
117/// pub struct MyObject;
118///
119/// impl MyObject {
120/// // ... Assume we've implemented all the methods used below
121/// }
122/// );
123///
124/// /// Creation methods.
125/// impl MyObject {
126/// extern_methods!(
127/// // SAFETY: The method is correctly specified.
128/// #[unsafe(method(new))]
129/// pub fn new() -> Retained<Self>;
130///
131/// // SAFETY: The method is correctly specified.
132/// #[unsafe(method(initWithVal:))]
133/// // arbitrary self types are not stable, but we can work around it
134/// // with the special name `this`.
135/// pub fn init(this: Allocated<Self>, val: usize) -> Retained<Self>;
136/// );
137/// }
138///
139/// /// Instance accessor methods.
140/// impl MyObject {
141/// extern_methods!(
142/// // SAFETY: The method is correctly specified.
143/// #[unsafe(method(foo))]
144/// pub fn foo(&self) -> NSUInteger;
145///
146/// // SAFETY: The method is correctly specified.
147/// #[unsafe(method(fooObject))]
148/// pub fn foo_object(&self) -> Retained<NSObject>;
149///
150/// // SAFETY: The method is correctly specified.
151/// #[unsafe(method(withError:_))]
152/// // Since the selector specifies "_", the return type is assumed to
153/// // be `Result`.
154/// pub fn with_error(&self) -> Result<(), Retained<NSError>>;
155/// );
156/// }
157/// ```
158///
159/// The `extern_methods!` then expands to (roughly):
160///
161/// ```
162/// # use objc2::encode::{Encode, Encoding};
163/// # use objc2::ffi::NSUInteger;
164/// # use objc2::rc::{Allocated, Retained};
165/// # use objc2::runtime::NSObject;
166/// # use objc2::{define_class, extern_methods, ClassType};
167/// #
168/// # // Shim
169/// # type NSError = NSObject;
170/// #
171/// # define_class!(
172/// # #[unsafe(super(NSObject))]
173/// # pub struct MyObject;
174/// #
175/// # impl MyObject {
176/// # // ... Assume we've implemented all the methods used below
177/// # }
178/// # );
179/// #
180/// use objc2::msg_send;
181///
182/// /// Creation methods.
183/// impl MyObject {
184/// pub fn new() -> Retained<Self> {
185/// unsafe { msg_send![Self::class(), new] }
186/// }
187///
188/// pub fn init(this: Allocated<Self>, val: usize) -> Retained<Self> {
189/// unsafe { msg_send![this, initWithVal: val] }
190/// }
191/// }
192///
193/// /// Instance accessor methods.
194/// impl MyObject {
195/// pub fn foo(&self) -> NSUInteger {
196/// unsafe { msg_send![self, foo] }
197/// }
198///
199/// pub fn foo_object(&self) -> Retained<NSObject> {
200/// unsafe { msg_send![self, fooObject] }
201/// }
202///
203/// // Since the selector specifies one more argument than we
204/// // have, the return type is assumed to be `Result`.
205/// pub fn with_error(&self) -> Result<(), Retained<NSError>> {
206/// unsafe { msg_send![self, withError: _] }
207/// }
208/// }
209/// ```
210///
211/// See the source code of `objc2-foundation` for many more examples.
212#[macro_export]
213macro_rules! extern_methods {
214 (
215 // Base case of the tt-muncher.
216 ) => {};
217
218 (
219 // Unsafe method.
220 //
221 // Special attributes:
222 // #[unsafe(method($($selector:tt)+))]
223 // #[unsafe(method_family = $family:ident)]
224 $(#[$($m:tt)*])*
225 $v:vis unsafe fn $fn_name:ident($($params:tt)*) $(-> $ret:ty)?
226 // Optionally, a single `where` bound.
227 // TODO: Handle this better.
228 $(where $($where:ty : $bound:path),+ $(,)?)?;
229
230 $($rest:tt)*
231 ) => {
232 $crate::__rewrite_self_param! {
233 ($($params)*)
234
235 ($crate::__extract_method_attributes)
236 ($(#[$($m)*])*)
237
238 ($crate::__extern_methods_method_out)
239 ($v unsafe fn $fn_name($($params)*) $(-> $ret)?)
240 ($($($where : $bound ,)+)?)
241 }
242
243 $crate::extern_methods!($($rest)*);
244 };
245
246 (
247 // Safe method.
248 //
249 // Special attributes:
250 // #[unsafe(method($($selector:tt)+))]
251 // #[unsafe(method_family = $family:ident)]
252 $(#[$($m:tt)*])*
253 $v:vis fn $fn_name:ident($($params:tt)*) $(-> $ret:ty)?
254 // Optionally, a single `where` bound.
255 // TODO: Handle this better.
256 $(where $($where:ty : $bound:path),+ $(,)?)?;
257
258 $($rest:tt)*
259 ) => {
260 $crate::__rewrite_self_param! {
261 ($($params)*)
262
263 ($crate::__extract_method_attributes)
264 ($(#[$($m)*])*)
265
266 ($crate::__extern_methods_method_out)
267 ($v fn $fn_name($($params)*) $(-> $ret)?)
268 ($($($where : $bound ,)+)?)
269 }
270
271 $crate::extern_methods!($($rest)*);
272 };
273
274 (
275 // Deprecated syntax.
276 $(#[$m:meta])*
277 unsafe impl $type:ty {
278 $($methods:tt)*
279 }
280
281 $($rest:tt)*
282 ) => {
283 const _: () = $crate::__macro_helpers::extern_methods_unsafe_impl();
284
285 $(#[$m])*
286 impl<$($t $(: $b $(+ $rest)*)?),*> $type {
287 $crate::extern_methods! {
288 $($methods)*
289 }
290 }
291
292 $crate::extern_methods!($($rest)*);
293 };
294}
295
296#[doc(hidden)]
297#[macro_export]
298macro_rules! __extern_methods_method_out {
299 {
300 ($($function_start:tt)*)
301 ($($where:ty : $bound:path ,)*)
302
303 ($__builder_method:ident)
304 ($receiver:expr)
305 ($__receiver_ty:ty)
306 ($($__params_prefix:tt)*)
307 ($($params_rest:tt)*)
308
309 ($method_or_method_id:ident($($sel:tt)*))
310 ($($method_family:tt)*)
311 ($($optional:tt)*)
312 ($($attr_method:tt)*)
313 ($($attr_use:tt)*)
314 } => {
315 $($attr_method)*
316 $($function_start)*
317 where
318 $($where : $bound,)*
319 {
320 $crate::__extern_methods_method_id_deprecated!($method_or_method_id($($sel)*));
321 $crate::__extern_methods_no_optional!($($optional)*);
322
323 #[allow(unused_unsafe)]
324 unsafe {
325 $crate::__method_msg_send! {
326 ($receiver)
327 ($($sel)*)
328 ($($params_rest)*)
329
330 ()
331 ()
332 ($($method_family)*)
333 }
334 }
335 }
336 };
337}
338
339#[doc(hidden)]
340#[macro_export]
341macro_rules! __extern_methods_no_optional {
342 () => {};
343 (#[optional]) => {
344 $crate::__macro_helpers::compile_error!(
345 "`#[optional]` is only supported in `extern_protocol!`"
346 )
347 };
348}
349
350#[doc(hidden)]
351#[macro_export]
352macro_rules! __extern_methods_method_id_deprecated {
353 (method($($sel:tt)*)) => {};
354 (method_id($($sel:tt)*)) => {{
355 #[deprecated = $crate::__macro_helpers::concat!(
356 "using #[unsafe(method_id(",
357 $crate::__macro_helpers::stringify!($($sel)*),
358 "))] inside extern_methods! is deprecated.\nUse #[unsafe(method(",
359 $crate::__macro_helpers::stringify!($($sel)*),
360 "))] instead",
361 )]
362 #[inline]
363 fn method_id() {}
364 method_id();
365 }};
366}
367
368#[cfg(test)]
369mod tests {
370 use crate::extern_methods;
371 use crate::runtime::{NSObject, NSObjectProtocol};
372
373 #[test]
374 fn outside_impl_using_this() {
375 // The fact that this works outside `impl` is an implementation detail
376 // that will get resolved once we have arbitrary self types.
377 extern_methods!(
378 #[unsafe(method(hash))]
379 fn obj_hash(this: &NSObject) -> usize;
380 );
381
382 let obj = NSObject::new();
383 assert_eq!(obj_hash(&obj), obj.hash())
384 }
385}