1use objc::declare::ClassDecl;
2use objc::runtime::{Class, Object, Sel};
3
4#[macro_export]
5macro_rules! add_pub_ivar {
6 (pub, $name:ident, $decl:expr, $type:ident) => {{
7 $decl.add_ivar::<$type>(concat!("_", stringify!($name)));
8 extern "C" fn getter(this: &Object, _cmd: Sel) -> $type {
9 unsafe { *this.get_ivar::<$type>(concat!("_", stringify!($name))) }
10 }
11 let getter_extern: extern "C" fn(&Object, Sel) -> $type = getter;
12 unsafe {
13 $decl.add_method(sel!($name), getter_extern);
14 }
15 extern "C" fn setter(this: &mut Object, _cmd: Sel, value: $type) {
16 unsafe {
17 this.set_ivar::<$type>(concat!("_", stringify!($name)), value);
18 }
19 }
20 let extern_setter: extern "C" fn(&mut Object, Sel, $type) = setter;
21 unsafe {
22 $decl.add_method(
23 sel_impl!(concat!(stringify!($name), ':', '\0')),
24 extern_setter,
25 );
26 }
27 concat!("_", stringify!($name))
28 }};
29 (priv, $name:ident, $decl:expr, $type:ident) => {{
30 $decl.add_ivar::<$type>(concat!("_", stringify!($name)));
31 concat!("_", stringify!($name))
32 }};
33}
34
35macro_rules! process_field {
36 ($class_dec:expr, (sel $($sel_name:ident :)* <- $fun_name:expr)) => {{
37 unsafe {
38 $class_dec.add_method(sel!($($sel_name :)*), $fun_name)
39 }
40 }};
41 ($class_dec:expr, (sel $($sel_name:ident : ($sel_type:ident) $sel_local_name:ident)*
42 -> $ret_type:ident with |$obj:ident, $sel:ident| $body:tt)) => {{
43 extern "C" fn selector_fun($obj: &mut Object, $sel: Sel, $($sel_local_name:$sel_type),*) -> $ret_type $body
44 let selector_fun_extern: extern "C" fn(&mut Object, Sel, $($sel_type),*) -> $ret_type = selector_fun;
45
46 unsafe {
47 $class_dec.add_method(sel!($($sel_name:)*), selector_fun_extern);
48 }
49 }};
50 ($class_dec:expr, (static sel $($sel_name:ident : ($sel_type:ident) $sel_local_name:ident)*
51 -> $ret_type:ident with |$cls:ident, $sel:ident| $body:tt)) => {{
52 extern "C" fn selector_fun($cls: &Class, $sel: Sel, $($sel_local_name:$sel_type),*) -> $ret_type $body
53 let selector_fun_extern: extern "C" fn(&Class, Sel, $($sel_type),*) -> $ret_type = selector_fun;
54
55 unsafe {
56 $class_dec.add_class_method(sel!($($sel_name:)*), selector_fun_extern);
57 }
58 }};
59 ($class_dec:expr, ($access:ident $field_name:ident : $ty_name:ident)) => {
60 add_pub_ivar!($access, $field_name, $class_dec, $ty_name);
61 };
62}
63
64#[macro_export]
65macro_rules! register_class {
66 ($name:ident : $parent:ident with {
67 $($addition:tt,)*
68 }) => {{
69 let superclass = class!($parent);
70 let mut my_class = ClassDecl::new(stringify!($name), superclass).unwrap();
71 $(
72 process_field!(my_class, $addition);
73 )*
75 my_class.register()
76 }};
77}
78
79#[cfg(test)]
80mod test {
81 use objc::rc::StrongPtr;
82 #[macro_use]
83 use super::*;
84
85 #[test]
86 fn register_test() {
87 extern "C" fn obj_method(obj: &mut Object, sel: Sel, i1: i32, i2: i32) -> i32 {
88 return 11;
89 }
90 let my_method: extern "C" fn(&mut Object, Sel, i32, i32) -> i32 = obj_method;
91
92 let my_box_class = register_class!(MyBox:NSObject with {
93 (pub width: u32),
94 (priv height: u32),
95 (sel getThing:withOtherThing: <- my_method),
96 (sel add:(i32)t1 with:(i32)t2 -> i32 with |obj, sel| {
97 return t1+t2;
98 }),
99 (static sel mul:(i32)t1 with:(i32)t2 -> i32 with |cls, sel| {
100 return t1*t2;
101 }),
102 });
103 let obj = unsafe {
104 let obj: *mut Object = msg_send![my_box_class, alloc];
105 let obj: *mut Object = msg_send![obj, init];
106 StrongPtr::new(obj)
107 };
108 let x: i32 = unsafe { msg_send![*obj, add:5i32 with:6i32] };
109
110 assert_eq!(x, 11);
111
112 let y: i32 = unsafe { msg_send![my_box_class, mul:5 with:6] };
113 assert_eq!(y, 30);
114 }
115}