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}