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
use std::{
    os::raw::c_int,
    mem,
};
use crate::{
    prelude::*,
    ruby::VALUE,
};

/// An `extern "C" fn` that can be used as a method in
/// [`Class::def_method`](struct.Class.html#method.def_method).
pub unsafe trait MethodFn {
    /// The number of arguments taken by `self`.
    const ARITY: c_int;

    /// Returns the raw function pointer for `self`.
    fn raw_fn(self) -> unsafe extern "C" fn() -> VALUE;
}

macro_rules! impl_trait {
    ($($a:expr $(,$args:ty)*;)+) => { $(
        impl_trait!(@fn $a, unsafe extern "C" fn(this: AnyObject $(,$args)*));
        impl_trait!(@fn $a,        extern "C" fn(this: AnyObject $(,$args)*));
    )+ };
    (@fn $a:expr, $($f:tt)+) => {
        unsafe impl<O: Object> MethodFn for $($f)+ -> O {
            const ARITY: c_int = $a;

            #[inline]
            fn raw_fn(self) -> unsafe extern "C" fn() -> VALUE {
                unsafe { mem::transmute(self) }
            }
        }
    };
}

impl_trait! {
    -2, Array;
    -1, c_int, *const AnyObject;
}

macro_rules! replace {
    ($_t:tt $sub:tt) => { $sub };
}

macro_rules! count {
    ($($t:tt)*) => { 0 $(+ replace!($t 1))* };
}

// This macro lets us create an implementation of `MethodFn` on a pair of
// `extern "C" fn` pairs (one being `unsafe`) for each comma token
macro_rules! impl_trait_many {
    () => {
        impl_trait! { 0; }
    };
    (, $($t:tt)*) => {
        impl_trait_many!($($t)*);
        impl_trait! { 1 + count!($($t)*), AnyObject $(, replace!($t AnyObject))* ; }
    };
}

// 15 is the maximum arity allowed
impl_trait_many!(,,,,, ,,,,, ,,,,,);