use std::{convert::TryFrom, fmt::Debug}; pub trait WitValue where Self: Sized + Debug + Copy, { } impl WitValue for i32 {} impl WitValue for f32 {} pub trait Function { fn to_callable(&self) -> &Func; } pub trait WitCall { fn call(self, function: &dyn Function); } macro_rules! do_it { ( $($x:ident),* ) => { // Implement `Function` for `Func` impl< Func $( , $x )* > Function for Func where Func: Fn( $( $x ),* ), $( $x: WitValue ),* { fn to_callable(&self) -> &Func { println!("size of me {}", ::std::mem::size_of::()); println!("size of usize {}", ::std::mem::size_of::()); self } } // Implement `WitCall` for tuples. impl< Func $( , $x )* > WitCall for ( $( $x ),* ) where Func: Fn( $( $x ),* ), $( $x: WitValue ),* { #[allow(non_snake_case, unused_parens)] fn call(self, function: &dyn Function) { let function: &dyn Fn( $( $x ),* ) = function.to_callable(); let ( $( $x ),* ) = self; function( $( $x ),* ) } } // Implement `call` on `dyn Function`. impl dyn Function where Func: Fn( $( $x ),* ), $( $x: WitValue ),* { #[allow(dead_code, non_snake_case)] fn call(&self $( , $x: $x )*) { ( $( $x ),* ).call(self); } } } } do_it!(); do_it!(A); do_it!(A, B); do_it!(A, B, C); do_it!(A, B, C, D); do_it!(A, B, C, D, E); do_it!(A, B, C, D, E, F); do_it!(A, B, C, D, E, F, G); do_it!(A, B, C, D, E, F, G, H); do_it!(A, B, C, D, E, F, G, H, I); do_it!(A, B, C, D, E, F, G, H, I, J); do_it!(A, B, C, D, E, F, G, H, I, J, K); do_it!(A, B, C, D, E, F, G, H, I, J, K, L); #[cfg(test)] #[test] fn test_functions() { // arity 0 ().call(&|| { println!("()"); }); let f0: &dyn Function<_, _> = &|| { println!("~~~> ()"); }; f0.call(); // arity 1 (42).call(&|a: i32| { println!("{:?}", a); }); let f1: &dyn Function<_, _> = &|a: i32| { println!("~~~> {:?}", a); }; f1.call(42); // arity 2 (1, 2).call(&|a: i32, b: i32| { println!("{:?}, {:?}", a, b); }); let f2: &dyn Function<_, _> = &|a: i32, b: i32| { println!("~~~> {:?}, {:?}", a, b); }; f2.call(1, 2); } pub enum Ty { I32, I64, F32, F64, } #[derive(Debug)] pub enum Value { I32(i32), I64(i64), F32(f32), F64(f64), } macro_rules! impl_value { ($native_type:ty, $value_variant:ident) => { impl TryFrom<&Value> for $native_type { type Error = &'static str; fn try_from(w: &Value) -> Result { match w { Value::$value_variant(n) => Ok(n.clone()), _ => Err("Invalid cast."), } } } }; } impl_value!(i32, I32); impl_value!(i64, I64); impl_value!(f32, F32); impl_value!(f64, F64); #[allow(unused)] macro_rules! any_to_value { ($variable:ident, $expected_ty:expr) => {{ use std::convert::TryInto; let value: Value = { let var_ref: &dyn Any = &$variable; match $expected_ty { Ty::I32 => var_ref .downcast_ref::() .and_then(|v| Some(Value::I32(*v))), Ty::I64 => var_ref .downcast_ref::() .and_then(|v| Some(Value::I64(*v))), Ty::F32 => var_ref .downcast_ref::() .and_then(|v| Some(Value::F32(*v))), Ty::F64 => var_ref .downcast_ref::() .and_then(|v| Some(Value::F64(*v))), } } .expect(&format!( "Failed to downcast the value of `{}`.", stringify!($variable) )); (&value).try_into().expect("Failed to cast.") }}; } #[cfg(test)] #[test] fn test_dynamically_typed_functions() { use std::any::Any; // more fun with dynamically typed inputs fn f() -> impl Fn(A, B) where A: Any + WitValue, B: Any + WitValue, { let a_expected_type = Ty::I32; let b_expected_type = Ty::I32; move |a: A, b: B| { let a: i32 = any_to_value!(a, a_expected_type); let b: i32 = any_to_value!(b, b_expected_type); println!("~~~> {:?} + {:?} = {:?}", a, b, a + b); } } let f2: &dyn Function<_, _> = &f(); f2.call(1i32, 2i32); }