Last active
October 11, 2019 13:17
-
-
Save Hywan/ce725569fd3534adee5f6f8d90f67a36 to your computer and use it in GitHub Desktop.
Safely and Dynamically distribute arguments on function
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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<Func, Inputs> { | |
fn to_callable(&self) -> &Func; | |
} | |
pub trait WitCall<Func, Inputs> { | |
fn call(self, function: &dyn Function<Func, Inputs>); | |
} | |
macro_rules! do_it { | |
( $($x:ident),* ) => { | |
// Implement `Function` for `Func` | |
impl< Func $( , $x )* > Function<Func, ( $( $x ),* )> for Func | |
where | |
Func: Fn( $( $x ),* ), | |
$( $x: WitValue ),* | |
{ | |
fn to_callable(&self) -> &Func { | |
println!("size of me {}", ::std::mem::size_of::<Self>()); | |
println!("size of usize {}", ::std::mem::size_of::<usize>()); | |
self | |
} | |
} | |
// Implement `WitCall` for tuples. | |
impl< Func $( , $x )* > WitCall<Func, ( $( $x ),* )> for ( $( $x ),* ) | |
where | |
Func: Fn( $( $x ),* ), | |
$( $x: WitValue ),* | |
{ | |
#[allow(non_snake_case, unused_parens)] | |
fn call(self, function: &dyn Function<Func, ( $( $x ),* )>) { | |
let function: &dyn Fn( $( $x ),* ) = function.to_callable(); | |
let ( $( $x ),* ) = self; | |
function( $( $x ),* ) | |
} | |
} | |
// Implement `call` on `dyn Function`. | |
impl<Func $( , $x )*> dyn Function<Func, ( $( $x ),* )> | |
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<Self, Self::Error> { | |
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::<i32>() | |
.and_then(|v| Some(Value::I32(*v))), | |
Ty::I64 => var_ref | |
.downcast_ref::<i64>() | |
.and_then(|v| Some(Value::I64(*v))), | |
Ty::F32 => var_ref | |
.downcast_ref::<f32>() | |
.and_then(|v| Some(Value::F32(*v))), | |
Ty::F64 => var_ref | |
.downcast_ref::<f64>() | |
.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<A, B>() -> 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); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment