indirect_once/
lib.rs

1#![allow(unused_imports)]
2
3#[cfg(feature = "proc-macro")]
4pub use indirect_once_derive::*;
5
6#[cfg(not(feature = "parking-lot"))]
7use std::sync::Once;
8#[cfg(feature = "parking-lot")]
9use parking_lot::Once;
10
11#[macro_export]
12macro_rules! indirect_fn {
13    (resolver = $resolver: ident ; fn $name:ident($($arg: ident : $pty: ty),*) {}) => { $crate::indirect_fn!(resolver = $resolver; fn $name($($arg: $pty),*) -> () {})};
14    (resolver = $resolver: ident ; fn $name:ident($($arg: ident : $pty: ty),*) -> $ret: ty {})=> {
15	fn $name($($arg: $pty),*) -> $ret {
16	    static mut IMPL: Option<&'static fn($($pty),*) -> $ret> = None;
17	    static INIT: Once = Once::new();
18
19	    unsafe {
20		INIT.call_once(|| {
21		    IMPL = Some($resolver());
22		});
23		(IMPL.unwrap())($($arg),*)
24	    }
25	}
26    }
27}
28
29#[cfg(test)]
30mod tests {
31    use super::*;
32
33    fn incr(x: i32) -> i32 {
34	x + 1
35    }
36
37    fn decr(x: i32) -> i32 {
38	x - 1
39    }
40    
41    fn foo() -> &'static fn(i32) -> i32 {
42	&(incr as fn(i32) -> i32)
43    }
44
45    fn bar() -> &'static fn(i32) -> i32 {
46	&(decr as fn(i32) -> i32)
47    }
48    
49    #[test]
50    #[cfg(feature = "proc-macro")]
51    fn it_works() {
52	#[indirect(resolver = "foo")]
53	fn hello_world(arg: i32) -> i32 {}
54
55	assert_eq!(hello_world(10), 11);
56    }
57
58    #[test]
59    #[cfg(feature = "proc-macro")]
60    fn it_works_2() {
61	#[indirect(resolver = "bar")]
62	fn hello_hello(arg: i32) -> i32 {}
63
64	assert_eq!(hello_hello(10), 9);
65    }
66
67    #[test]
68    #[cfg(feature = "proc-macro")]
69    fn multiple_arguments() {
70	fn do_thingy(one: i32, two: i32, really: bool) -> (bool, i32) {
71	    if really {
72		(really, one + two)
73	    } else {
74		(really, two)
75	    }
76	}
77
78	fn pick() -> &'static fn(i32, i32, bool) -> (bool, i32) {
79	    &(do_thingy as _)
80	}
81
82	#[indirect(resolver = "pick")]
83	fn foo(one: i32, two: i32, three: bool) -> (bool, i32) {}
84
85	assert_eq!(foo(1, 2, true), (true, 3))
86    }
87
88    #[test]
89    fn macro_impl() {
90	indirect_fn! {
91	    resolver = foo; fn dog(param: i32) -> i32 {}
92	}
93
94	assert_eq!(dog(41), 42);
95    }
96}