Skip to content

Instantly share code, notes, and snippets.

@Hywan
Last active October 11, 2019 13:17
Show Gist options
  • Save Hywan/ce725569fd3534adee5f6f8d90f67a36 to your computer and use it in GitHub Desktop.
Save Hywan/ce725569fd3534adee5f6f8d90f67a36 to your computer and use it in GitHub Desktop.
Safely and Dynamically distribute arguments on function
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