Add FnPtr::call.
This commit is contained in:
parent
d56585c877
commit
30bfdd841a
@ -13,6 +13,7 @@ Enhancements
|
|||||||
------------
|
------------
|
||||||
|
|
||||||
* Added `into_array` and `into_typed_array` for `Dynamic`.
|
* Added `into_array` and `into_typed_array` for `Dynamic`.
|
||||||
|
* New `FnPtr::call` to simplify calling a function pointer.
|
||||||
|
|
||||||
Deprecated API's
|
Deprecated API's
|
||||||
----------------
|
----------------
|
||||||
|
@ -9,7 +9,6 @@ use std::prelude::v1::*;
|
|||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Call a script function defined in an [`AST`] with multiple arguments.
|
/// Call a script function defined in an [`AST`] with multiple arguments.
|
||||||
/// Arguments are passed as a tuple.
|
|
||||||
///
|
///
|
||||||
/// Not available under `no_function`.
|
/// Not available under `no_function`.
|
||||||
///
|
///
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
//! The `FnPtr` type.
|
//! The `FnPtr` type.
|
||||||
|
|
||||||
use crate::tokenizer::is_valid_identifier;
|
use crate::tokenizer::is_valid_identifier;
|
||||||
|
use crate::types::dynamic::Variant;
|
||||||
use crate::{
|
use crate::{
|
||||||
Dynamic, EvalAltResult, Identifier, NativeCallContext, Position, RhaiResult, StaticVec,
|
Dynamic, Engine, EvalAltResult, FuncArgs, Identifier, NativeCallContext, Position, RhaiResult,
|
||||||
|
StaticVec, AST,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
use std::{
|
||||||
|
any::type_name,
|
||||||
convert::{TryFrom, TryInto},
|
convert::{TryFrom, TryInto},
|
||||||
fmt, mem,
|
fmt, mem,
|
||||||
};
|
};
|
||||||
@ -96,8 +99,69 @@ impl FnPtr {
|
|||||||
self.0.starts_with(crate::engine::FN_ANONYMOUS)
|
self.0.starts_with(crate::engine::FN_ANONYMOUS)
|
||||||
}
|
}
|
||||||
/// Call the function pointer with curried arguments (if any).
|
/// Call the function pointer with curried arguments (if any).
|
||||||
|
/// The function may be script-defined (not available under `no_function`) or native Rust.
|
||||||
///
|
///
|
||||||
/// If this function is a script-defined function, it must not be marked private.
|
/// This method is intended for calling a function pointer that is passed into a native Rust
|
||||||
|
/// function as an argument. Therefore, the [`AST`] is _NOT_ evaluated before calling the
|
||||||
|
/// function.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||||
|
/// # #[cfg(not(feature = "no_function"))]
|
||||||
|
/// # {
|
||||||
|
/// use rhai::{Engine, FnPtr};
|
||||||
|
///
|
||||||
|
/// let engine = Engine::new();
|
||||||
|
///
|
||||||
|
/// let ast = engine.compile("fn foo(x, y) { len(x) + y }")?;
|
||||||
|
///
|
||||||
|
/// let mut fn_ptr = FnPtr::new("foo")?;
|
||||||
|
///
|
||||||
|
/// // Curry values into the function pointer
|
||||||
|
/// fn_ptr.set_curry(vec!["abc".into()]);
|
||||||
|
///
|
||||||
|
/// // Values are only needed for non-curried parameters
|
||||||
|
/// let result: i64 = fn_ptr.call(&engine, &ast, ( 39_i64, ) )?;
|
||||||
|
///
|
||||||
|
/// assert_eq!(result, 42);
|
||||||
|
/// # }
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn call<T: Variant + Clone>(
|
||||||
|
&self,
|
||||||
|
engine: &Engine,
|
||||||
|
ast: &AST,
|
||||||
|
args: impl FuncArgs,
|
||||||
|
) -> Result<T, Box<EvalAltResult>> {
|
||||||
|
let mut arg_values = crate::StaticVec::new_const();
|
||||||
|
args.parse(&mut arg_values);
|
||||||
|
|
||||||
|
let lib = [ast.as_ref()];
|
||||||
|
let ctx = NativeCallContext::new(engine, self.fn_name(), &lib);
|
||||||
|
|
||||||
|
let result = self.call_dynamic(&ctx, None, arg_values)?;
|
||||||
|
|
||||||
|
let typ = engine.map_type_name(result.type_name());
|
||||||
|
|
||||||
|
result.try_cast().ok_or_else(|| {
|
||||||
|
EvalAltResult::ErrorMismatchOutputType(
|
||||||
|
engine.map_type_name(type_name::<T>()).into(),
|
||||||
|
typ.into(),
|
||||||
|
Position::NONE,
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/// Call the function pointer with curried arguments (if any).
|
||||||
|
/// The function may be script-defined (not available under `no_function`) or native Rust.
|
||||||
|
///
|
||||||
|
/// This method is intended for calling a function pointer that is passed into a native Rust
|
||||||
|
/// function as an argument. Therefore, the [`AST`] is _NOT_ evaluated before calling the
|
||||||
|
/// function.
|
||||||
///
|
///
|
||||||
/// # WARNING
|
/// # WARNING
|
||||||
///
|
///
|
||||||
@ -110,8 +174,9 @@ impl FnPtr {
|
|||||||
&self,
|
&self,
|
||||||
ctx: &NativeCallContext,
|
ctx: &NativeCallContext,
|
||||||
this_ptr: Option<&mut Dynamic>,
|
this_ptr: Option<&mut Dynamic>,
|
||||||
mut arg_values: impl AsMut<[Dynamic]>,
|
arg_values: impl AsMut<[Dynamic]>,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
|
let mut arg_values = arg_values;
|
||||||
let mut arg_values = arg_values.as_mut();
|
let mut arg_values = arg_values.as_mut();
|
||||||
let mut args_data;
|
let mut args_data;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use rhai::{Engine, EvalAltResult, INT};
|
use rhai::{Engine, EvalAltResult, FnPtr, INT};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fn_ptr() -> Result<(), Box<EvalAltResult>> {
|
fn test_fn_ptr() -> Result<(), Box<EvalAltResult>> {
|
||||||
@ -111,3 +111,43 @@ fn test_fn_ptr_curry() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
fn test_fn_ptr_call() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
let ast = engine.compile("private fn foo(x, y) { len(x) + y }")?;
|
||||||
|
|
||||||
|
let mut fn_ptr = FnPtr::new("foo")?;
|
||||||
|
fn_ptr.set_curry(vec!["abc".into()]);
|
||||||
|
let result: INT = fn_ptr.call(&engine, &ast, (39 as INT,))?;
|
||||||
|
|
||||||
|
assert_eq!(result, 42);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
fn test_fn_ptr_make_closure() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let f = {
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
let ast = engine.compile(
|
||||||
|
r#"
|
||||||
|
let test = "hello";
|
||||||
|
|x| test + x // this creates a closure
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let fn_ptr = engine.eval_ast::<FnPtr>(&ast)?;
|
||||||
|
|
||||||
|
move |x: INT| -> Result<String, Box<EvalAltResult>> { fn_ptr.call(&engine, &ast, (x,)) }
|
||||||
|
};
|
||||||
|
|
||||||
|
// 'f' captures: the Engine, the AST, and the closure
|
||||||
|
assert_eq!(f(42)?, "hello42");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user