1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*!
  This crate provides the ability to create overloadable functions in rust through
  the use of a macro.

  # Syntax:

  ```no_run
  use overloadable::overload;
  overload!{
      #[my_meta]
      visibility function_name as
      fn<OptionalTypeArgs>(function_params: function_types) -> optional_return_type where [OptionalTypeArgs: constraints] {
          code_body
      }
  }
  ```

  # What is produced

  Here is an example of the output produced by `overloadable`:
  ```
  use overloadable::overload;
  use std::fmt::Debug;
  overload!{
      pub my_func as
      fn(x: usize, y: &str) -> f32 {
          (x * y.len()) as f32
      }
      fn<T>() where [T: Debug] {}
  }
  //Gives
  #[allow(non_camel_case_types)]
  pub struct my_func;
  impl Fn<(usize, &str,)> for my_func {
      extern "rust-call" fn call(&self, (x, y,): (usize, &str,)) -> f32 {
          {
              (x * y.len()) as f32
          }
      }
  }
  //The rest of the `Fn*` family
  impl<T> Fn<()> for my_func where T: Debug {
      extern "rust-call" fn call(&self, (): ()) -> () {
          {}
      }
  }
  //The rest of the `Fn*` family.
  ```

  # Limitations
  - These functions cannot be exposed for ffi.
  - These functions cannot be `const fn`s.
  - These functions cannot pattern match their arguments.
  - These functions cannot be used in place of a `fn()`-style function pointer
    - But they can be used with the `Fn*` family of traits.
  - The `where` clause' contents must be surrounded by square brackets due to a constraint in macros.
  - Generic lifetime parameters must always be proceeded by a comma, even if they are the only generic parameters.

*/

#![feature(unboxed_closures, fn_traits)]
#[macro_export]
macro_rules! overload {
    ($v:vis $name:ident as
    $(
        $(#[$($m:tt)*])*
        fn
            $(
                <$($lt:lifetime,)*
                 $($gen:ident),*$(,)?>
            )?
            (
                $($param:ident : $param_type:ty$(,)?)*
            )
            $( -> $ret:ty)?
            $(where [$($cons:tt)*])?
        $code:block
    )*) => {

        #[allow(non_camel_case_types)]
        $v struct $name;
        $(
            impl$(<$($lt,)*$($gen)?>)? ::std::ops::Fn<($($param_type,)*)> for $name where $($($cons)*)? {
                $(#[$($m)*])*
                extern "rust-call" fn call(&self, ($($param,)*): ($($param_type,)*)) -> <Self as FnOnce<($($param_type,)*)>>::Output {
                    $code
                }
            }

            impl$(<$($lt,)*$($gen)?>)? ::std::ops::FnMut<($($param_type,)*)> for $name where $($($cons)*)? {
                $(#[$($m)*])*
                extern "rust-call" fn call_mut(&mut self, x: ($($param_type,)*)) -> <Self as FnOnce<($($param_type,)*)>>::Output {
                    <Self as Fn<($($param_type,)*)>>::call(self, x)
                }
            }

            impl$(<$($lt,)*$($gen)?>)? ::std::ops::FnOnce<($($param_type,)*)> for $name where $($($cons)*)? {
                type Output = ($($ret)?);
                $(#[$($m)*])*
                extern "rust-call" fn call_once(self, x: ($($param_type,)*)) -> <Self as FnOnce<($($param_type,)*)>>::Output {
                    <Self as Fn<($($param_type,)*)>>::call(&self, x)
                }
            }
        )+
    }
}

#[cfg(test)]
mod tests {
    use std::fmt::Debug;
    overload! {
        pub(self) func_name as
        #[inline(always)]
        fn(x: usize, y: &str) -> f32 {
            (x * y.len()) as f32
        }
        #[inline(never)]
        fn(z: usize, y: bool) -> isize {
            if y {
                z as _
            } else {
                z as isize * -1
            }
        }
        #[no_mangle]
        fn() {
            println!("Abc");
        }
        fn<'a,>(x: &'a usize, y: &'a usize, z: &'a usize) {}
        fn<T>(x: &T) -> String where [T: Debug] {
            format!("{:?}", x)
        }
        fn<'a, T>(x: &'a T, y: &'a T) where [T: Debug] {}
    }

    #[test]
    fn it_works() {
        assert_eq!(func_name(2, "abc"), 6.0f32);
        assert_eq!(func_name(3, false), -3);
        assert_eq!(func_name(), ());
        assert_eq!(func_name(&String::from("foo")), String::from(r#""foo""#));
    }
}