ifunky/
lib.rs

1#[doc(hidden)]
2pub use std::sync::{Once, ONCE_INIT};
3
4#[macro_export]
5macro_rules! ifunky {
6    ($(pub fn $fun:ident($($arg:ident: $kind:ty),*) -> $ret:ty { $($selector:expr);* })*) => (
7        $(
8            #[inline(always)]
9            pub fn $fun($($arg: $kind),*) -> $ret {
10                static SYNCHRO_START: $crate::Once = $crate::ONCE_INIT;
11                static mut INDIRECT: fn( $($kind),* ) -> $ret = dispatch;
12
13                fn dispatch($($arg: $kind),*) -> $ret {
14                    fn select() -> fn( $($kind),* ) -> $ret {
15                        $($selector);*
16                    }
17
18                    SYNCHRO_START.call_once(
19                        || unsafe { INDIRECT = select() }
20                    );
21
22
23                    $fun($($arg),*)
24                }
25
26                (unsafe { INDIRECT })( $($arg),* )
27            }
28        )*
29    );
30}
31
32#[cfg(test)]
33mod test {
34    extern crate rand;
35    use std::iter::repeat;
36
37    ifunky! {
38        pub fn foo(x: u32) -> u32 {
39            if rand::random::<bool>() {
40                foo_big as fn(u32) -> u32
41            } else {
42                foo_bigger as fn(u32) -> u32
43            }
44        }
45
46        pub fn bar(x: u32) -> u32 {
47            if rand::random::<bool>() {
48                bar_small as fn(u32) -> u32
49            } else {
50                bar_smaller as fn(u32) -> u32
51            }
52        }
53    }
54
55    fn foo_big(x: u32) -> u32 {
56        x + 1
57    }
58
59    fn foo_bigger(x: u32) -> u32 {
60        (x + 1) * 2
61    }
62
63    fn bar_small(x: u32) -> u32 {
64        x - 1
65    }
66
67    fn bar_smaller(x: u32) -> u32 {
68        (x - 1) / 2
69    }
70
71    #[test]
72    fn memoizes_foo() {
73        let gold_master_foo = foo(3);
74        assert!(
75            repeat( 3 )
76            .map( foo )
77            .take( 999 )
78            .all(|x| x == gold_master_foo)
79        );
80    }
81
82    #[test]
83    fn memoizes_bar() {
84        let gold_master_bar = bar(3);
85        assert!(
86            repeat( 3 )
87            .map( bar )
88            .take( 999 )
89            .all(|x| x == gold_master_bar)
90        );
91    }
92}