Add new register_fn_raw API.
This commit is contained in:
parent
4052ad3df1
commit
a27f89b524
@ -14,6 +14,7 @@ Breaking changes
|
||||
----------------
|
||||
|
||||
* `EvalAltResult::ErrorMismatchOutputType` has an extra argument containing the name of the requested type.
|
||||
* `Engine::call_fn_dynamic` take an extra argument, allowing a `Dynamic` value to be bound to the `this` pointer.
|
||||
|
||||
New features
|
||||
------------
|
||||
@ -22,6 +23,7 @@ New features
|
||||
This is particularly useful when converting a Rust `struct` to a `Dynamic` _object map_ and back.
|
||||
* `Engine::disable_symbol` to surgically disable keywords and/or operators.
|
||||
* `Engine::register_custom_operator` to define a custom operator.
|
||||
* New low-level API `Engine::register_raw_fn`.
|
||||
|
||||
|
||||
Version 0.16.1
|
||||
|
@ -55,10 +55,35 @@ let result: i64 = engine.call_fn(&mut scope, &ast, "hello", () )?;
|
||||
let result: () = engine.call_fn(&mut scope, &ast, "hidden", ())?;
|
||||
```
|
||||
|
||||
|
||||
`Engine::call_fn_dynamic`
|
||||
------------------------
|
||||
|
||||
For more control, construct all arguments as `Dynamic` values and use `Engine::call_fn_dynamic`, passing it
|
||||
anything that implements `IntoIterator<Item = Dynamic>` (such as a simple `Vec<Dynamic>`):
|
||||
|
||||
```rust
|
||||
let result: Dynamic = engine.call_fn_dynamic(&mut scope, &ast, "hello",
|
||||
vec![ String::from("abc").into(), 123_i64.into() ])?;
|
||||
let result: Dynamic = engine.call_fn_dynamic(
|
||||
&mut scope, // scope to use
|
||||
&ast, // AST to use
|
||||
"hello", // function entry-point
|
||||
None, // 'this' pointer, if any
|
||||
[ String::from("abc").into(), 123_i64.into() ])?; // arguments
|
||||
```
|
||||
|
||||
|
||||
Binding the `this` Pointer
|
||||
-------------------------
|
||||
|
||||
`Engine::call_fn_dynamic` can also bind a value to the `this` pointer of a script-defined function.
|
||||
|
||||
```rust
|
||||
let ast = engine.compile("fn action(x) { this += x; }")?;
|
||||
|
||||
let mut value: Dynamic = 1_i64.into();
|
||||
|
||||
let result = engine.call_fn_dynamic(&mut scope, &ast, "action", Some(&mut value), [ 41_i64.into() ])?;
|
||||
// ^^^^^^^^^^^^^^^^ binding the 'this' pointer
|
||||
|
||||
assert_eq!(value.as_int().unwrap(), 42);
|
||||
```
|
||||
|
68
src/api.rs
68
src/api.rs
@ -32,6 +32,38 @@ use crate::stdlib::{fs::File, io::prelude::*, path::PathBuf};
|
||||
|
||||
/// Engine public API
|
||||
impl Engine {
|
||||
/// Register a function with the `Engine`.
|
||||
///
|
||||
/// ## WARNING - Low Level API
|
||||
///
|
||||
/// This function is very low level. It takes a list of `TypeId`'s indicating the actual types of the parameters.
|
||||
///
|
||||
/// Arguments are simply passed in as a mutable array of `&mut Dynamic`.
|
||||
/// The arguments are guaranteed to be of the correct types matching the `TypeId`'s.
|
||||
///
|
||||
/// To get access to a primary parameter value (i.e. cloning is cheap), use: `args[n].clone().cast::<T>()`
|
||||
///
|
||||
/// To get access to a parameter value and avoid cloning, use `std::mem::take(args[n]).cast::<T>()`.
|
||||
/// Notice that this will _consume_ the argument, replacing it with `()`.
|
||||
///
|
||||
/// To get access to the first mutable parameter, use `args.get_mut(0).unwrap()`
|
||||
#[deprecated(note = "this function is volatile and may change")]
|
||||
pub fn register_raw_fn(
|
||||
&mut self,
|
||||
name: &str,
|
||||
args: &[TypeId],
|
||||
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&Engine, &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ 'static,
|
||||
|
||||
#[cfg(feature = "sync")] func: impl Fn(&Engine, &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
) {
|
||||
self.global_module.set_fn_var_args(name, args, func);
|
||||
}
|
||||
|
||||
/// Register a custom type for use with the `Engine`.
|
||||
/// The type must implement `Clone`.
|
||||
///
|
||||
@ -1127,7 +1159,7 @@ impl Engine {
|
||||
args: A,
|
||||
) -> Result<T, Box<EvalAltResult>> {
|
||||
let mut arg_values = args.into_vec();
|
||||
let result = self.call_fn_dynamic_raw(scope, ast, name, arg_values.as_mut())?;
|
||||
let result = self.call_fn_dynamic_raw(scope, ast, name, &mut None, arg_values.as_mut())?;
|
||||
|
||||
let typ = self.map_type_name(result.type_name());
|
||||
|
||||
@ -1140,7 +1172,15 @@ impl Engine {
|
||||
});
|
||||
}
|
||||
|
||||
/// Call a script function defined in an [`AST`] with multiple `Dynamic` arguments.
|
||||
/// Call a script function defined in an [`AST`] with multiple `Dynamic` arguments
|
||||
/// and optionally a value for binding to the 'this' pointer.
|
||||
///
|
||||
/// ## WARNING
|
||||
///
|
||||
/// All the arguments are _consumed_, meaning that they're replaced by `()`.
|
||||
/// This is to avoid unnecessarily cloning the arguments.
|
||||
/// Do not use the arguments after this call. If they are needed afterwards,
|
||||
/// clone them _before_ calling this function.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -1148,7 +1188,7 @@ impl Engine {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// # #[cfg(not(feature = "no_function"))]
|
||||
/// # {
|
||||
/// use rhai::{Engine, Scope};
|
||||
/// use rhai::{Engine, Scope, Dynamic};
|
||||
///
|
||||
/// let engine = Engine::new();
|
||||
///
|
||||
@ -1156,20 +1196,27 @@ impl Engine {
|
||||
/// fn add(x, y) { len(x) + y + foo }
|
||||
/// fn add1(x) { len(x) + 1 + foo }
|
||||
/// fn bar() { foo/2 }
|
||||
/// fn action(x) { this += x; } // function using 'this' pointer
|
||||
/// ")?;
|
||||
///
|
||||
/// let mut scope = Scope::new();
|
||||
/// scope.push("foo", 42_i64);
|
||||
///
|
||||
/// // Call the script-defined function
|
||||
/// let result = engine.call_fn_dynamic(&mut scope, &ast, "add", vec![ String::from("abc").into(), 123_i64.into() ])?;
|
||||
/// let result = engine.call_fn_dynamic(&mut scope, &ast, "add", None, [ String::from("abc").into(), 123_i64.into() ])?;
|
||||
/// // ^^^^ no 'this' pointer
|
||||
/// assert_eq!(result.cast::<i64>(), 168);
|
||||
///
|
||||
/// let result = engine.call_fn_dynamic(&mut scope, &ast, "add1", vec![ String::from("abc").into() ])?;
|
||||
/// let result = engine.call_fn_dynamic(&mut scope, &ast, "add1", None, [ String::from("abc").into() ])?;
|
||||
/// assert_eq!(result.cast::<i64>(), 46);
|
||||
///
|
||||
/// let result= engine.call_fn_dynamic(&mut scope, &ast, "bar", vec![])?;
|
||||
/// let result = engine.call_fn_dynamic(&mut scope, &ast, "bar", None, [])?;
|
||||
/// assert_eq!(result.cast::<i64>(), 21);
|
||||
///
|
||||
/// let mut value: Dynamic = 1_i64.into();
|
||||
/// let result = engine.call_fn_dynamic(&mut scope, &ast, "action", Some(&mut value), [ 41_i64.into() ])?;
|
||||
/// // ^^^^^^^^^^^^^^^^ binding the 'this' pointer
|
||||
/// assert_eq!(value.as_int().unwrap(), 42);
|
||||
/// # }
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
@ -1180,10 +1227,10 @@ impl Engine {
|
||||
scope: &mut Scope,
|
||||
ast: &AST,
|
||||
name: &str,
|
||||
arg_values: impl IntoIterator<Item = Dynamic>,
|
||||
mut this_ptr: Option<&mut Dynamic>,
|
||||
mut arg_values: impl AsMut<[Dynamic]>,
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let mut arg_values: StaticVec<_> = arg_values.into_iter().collect();
|
||||
self.call_fn_dynamic_raw(scope, ast, name, arg_values.as_mut())
|
||||
self.call_fn_dynamic_raw(scope, ast, name, &mut this_ptr, arg_values.as_mut())
|
||||
}
|
||||
|
||||
/// Call a script function defined in an [`AST`] with multiple `Dynamic` arguments.
|
||||
@ -1200,6 +1247,7 @@ impl Engine {
|
||||
scope: &mut Scope,
|
||||
ast: &AST,
|
||||
name: &str,
|
||||
this_ptr: &mut Option<&mut Dynamic>,
|
||||
arg_values: &mut [Dynamic],
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
||||
@ -1220,7 +1268,7 @@ impl Engine {
|
||||
&mut mods,
|
||||
&mut state,
|
||||
ast.lib(),
|
||||
&mut None,
|
||||
this_ptr,
|
||||
name,
|
||||
fn_def,
|
||||
args,
|
||||
|
@ -12,7 +12,7 @@ use crate::stdlib::{
|
||||
borrow::Cow,
|
||||
boxed::Box,
|
||||
char,
|
||||
collections::{HashMap, HashSet},
|
||||
collections::HashMap,
|
||||
fmt,
|
||||
iter::Peekable,
|
||||
str::{Chars, FromStr},
|
||||
|
@ -1,5 +1,7 @@
|
||||
#![cfg(not(feature = "no_function"))]
|
||||
use rhai::{Engine, EvalAltResult, Func, ParseError, ParseErrorType, Scope, INT};
|
||||
use rhai::{
|
||||
Dynamic, Engine, EvalAltResult, Func, ImmutableString, ParseError, ParseErrorType, Scope, INT,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_fn() -> Result<(), Box<EvalAltResult>> {
|
||||
@ -110,3 +112,47 @@ fn test_anonymous_fn() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fn_ptr() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut engine = Engine::new();
|
||||
|
||||
let ast = engine.compile(
|
||||
r#"
|
||||
fn foo(x) { this += x; }
|
||||
|
||||
let x = 41;
|
||||
x.bar("foo", 1);
|
||||
x
|
||||
"#,
|
||||
)?;
|
||||
let ast_clone = ast.clone();
|
||||
|
||||
engine.register_raw_fn(
|
||||
"bar",
|
||||
&[
|
||||
std::any::TypeId::of::<INT>(),
|
||||
std::any::TypeId::of::<ImmutableString>(),
|
||||
std::any::TypeId::of::<INT>(),
|
||||
],
|
||||
move |engine: &Engine, args: &mut [&mut Dynamic]| {
|
||||
let callback = args[1].clone().cast::<ImmutableString>();
|
||||
let value = args[2].clone();
|
||||
let this_ptr = args.get_mut(0).unwrap();
|
||||
|
||||
engine.call_fn_dynamic(
|
||||
&mut Scope::new(),
|
||||
&ast_clone,
|
||||
&callback,
|
||||
Some(this_ptr),
|
||||
[value],
|
||||
)?;
|
||||
|
||||
Ok(().into())
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(engine.eval_ast::<INT>(&ast)?, 42);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user