Avoid copying arguments for function calls.

This commit is contained in:
Stephen Chung
2020-05-03 16:54:24 +08:00
parent fc66a7ecef
commit d83b829810
8 changed files with 81 additions and 64 deletions

View File

@@ -7,7 +7,7 @@ use crate::engine::{calc_fn_spec, Engine, FnCallArgs};
use crate::result::EvalAltResult;
use crate::token::Position;
use crate::stdlib::{any::TypeId, boxed::Box, string::ToString};
use crate::stdlib::{any::TypeId, boxed::Box, mem, string::ToString};
/// A trait to register custom functions with the `Engine`.
pub trait RegisterFn<FN, ARGS, RET> {
@@ -115,16 +115,19 @@ pub trait RegisterResultFn<FN, ARGS, RET> {
pub struct Mut<T>(T);
//pub struct Ref<T>(T);
/// Identity dereferencing function.
/// Dereference into &mut.
#[inline]
pub fn identity<T>(data: &mut T) -> &mut T {
data
pub fn by_ref<T: Clone + 'static>(data: &mut Dynamic) -> &mut T {
// Directly cast the &mut Dynamic into &mut T to access the underlying data.
data.downcast_mut::<T>().unwrap()
}
/// Clone dereferencing function.
/// Dereference into value.
#[inline]
pub fn cloned<T: Clone>(data: &mut T) -> T {
data.clone()
pub fn by_value<T: Clone + 'static>(data: &mut Dynamic) -> T {
// We consume the argument and then replace it with () - the argument is not supposed to be used again.
// This way, we avoid having to clone the argument again, because it is already a clone when passed here.
mem::replace(data, Default::default()).cast::<T>()
}
/// This macro counts the number of arguments via recursion.
@@ -135,7 +138,7 @@ macro_rules! count_args {
/// This macro creates a closure wrapping a registered function.
macro_rules! make_func {
($fn_name:ident : $fn:ident : $map:expr ; $($par:ident => $clone:expr),*) => {
($fn_name:ident : $fn:ident : $map:expr ; $($par:ident => $convert:expr),*) => {
// ^ function name
// ^ function pointer
// ^ result mapping function
@@ -153,14 +156,16 @@ macro_rules! make_func {
#[allow(unused_variables, unused_mut)]
let mut drain = args.iter_mut();
$(
// Downcast every element, return in case of a type mismatch
let $par: &mut $par = drain.next().unwrap().downcast_mut().unwrap();
// Downcast every element, panic in case of a type mismatch (which shouldn't happen).
// Call the user-supplied function using ($convert) to access it either by value or by reference.
let $par = ($convert)(drain.next().unwrap());
)*
// Call the user-supplied function using ($clone) to
// potentially clone the value, otherwise pass the reference.
let r = $fn($(($clone)($par)),*);
$map(r, pos)
// Call the function with each parameter value
let r = $fn($($par),*);
// Map the result
$map(r, pos)
};
};
}
@@ -259,18 +264,17 @@ macro_rules! def_register {
//def_register!(imp_pop $($par => $mark => $param),*);
};
($p0:ident $(, $p:ident)*) => {
def_register!(imp $p0 => $p0 => $p0 => cloned $(, $p => $p => $p => cloned)*);
def_register!(imp $p0 => Mut<$p0> => &mut $p0 => identity $(, $p => $p => $p => cloned)*);
def_register!(imp $p0 => $p0 => $p0 => by_value $(, $p => $p => $p => by_value)*);
def_register!(imp $p0 => Mut<$p0> => &mut $p0 => by_ref $(, $p => $p => $p => by_value)*);
// handle the first parameter ^ first parameter passed through
// ^ others passed by value (cloned)
// ^ others passed by value (by_value)
// Currently does not support first argument which is a reference, as there will be
// conflicting implementations since &T: Any and T: Any cannot be distinguished
//def_register!(imp $p0 => Ref<$p0> => &$p0 => identity $(, $p => $p => $p => Clone::clone)*);
//def_register!(imp $p0 => Ref<$p0> => &$p0 => by_ref $(, $p => $p => $p => by_value)*);
def_register!($($p),*);
};
}
#[rustfmt::skip]
def_register!(A, B, C, D, E, F, G, H, J, K, L, M, N, P, Q, R, S, T, U, V);