objc_macros/
class.rs

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            //add_pub_ivar!($access, $field_name,$ty_name);
74        )*
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}