Add example to function pointer.

This commit is contained in:
Stephen Chung 2020-10-15 20:05:23 +08:00
parent a6fa94d946
commit ea9ef1091a
4 changed files with 62 additions and 10 deletions

View File

@ -114,7 +114,8 @@ Any custom syntax must include an _implementation_ of it.
The function signature of an implementation is: The function signature of an implementation is:
> `Fn(scope: &mut Scope, context: &mut EvalContext, inputs: &[Expression]) -> Result<Dynamic, Box<EvalAltResult>>` > `Fn(scope: &mut Scope, context: &mut EvalContext, inputs: &[Expression])`
> `-> Result<Dynamic, Box<EvalAltResult>>`
where: where:

View File

@ -62,7 +62,8 @@ Function Signature
The function signature passed to `Engine::on_var` takes the following form: The function signature passed to `Engine::on_var` takes the following form:
> `Fn(name: &str, index: usize, scope: &Scope, context: &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>> + 'static` > `Fn(name: &str, index: usize, scope: &Scope, context: &EvalContext)`
> `-> Result<Option<Dynamic>, Box<EvalAltResult>> + 'static`
where: where:

View File

@ -13,7 +13,7 @@ Built-in methods
---------------- ----------------
The following standard methods (mostly defined in the [`BasicFnPackage`][packages] but excluded if The following standard methods (mostly defined in the [`BasicFnPackage`][packages] but excluded if
using a [raw `Engine`]) operate on [strings]: using a [raw `Engine`]) operate on function pointers:
| Function | Parameter(s) | Description | | Function | Parameter(s) | Description |
| -------------------------- | ------------ | ---------------------------------------------------------------------------- | | -------------------------- | ------------ | ---------------------------------------------------------------------------- |
@ -64,7 +64,7 @@ Global Namespace Only
Because of their dynamic nature, function pointers cannot refer to functions in [`import`]-ed [modules]. Because of their dynamic nature, function pointers cannot refer to functions in [`import`]-ed [modules].
They can only refer to functions within the global [namespace][function namespace]. They can only refer to functions within the global [namespace][function namespace].
See [function namespaces] for more details. See _[Function Namespaces]_ for more details.
```rust ```rust
import "foo" as f; // assume there is 'f::do_work()' import "foo" as f; // assume there is 'f::do_work()'
@ -135,13 +135,13 @@ map[func].call(42);
``` ```
Binding the `this` Pointer Bind the `this` Pointer
------------------------- ----------------------
When `call` is called as a _method_ but not on a `FnPtr` value, it is possible to dynamically dispatch When `call` is called as a _method_ but not on a function pointer, it is possible to dynamically dispatch
to a function call while binding the object in the method call to the `this` pointer of the function. to a function call while binding the object in the method call to the `this` pointer of the function.
To achieve this, pass the `FnPtr` value as the _first_ argument to `call`: To achieve this, pass the function pointer as the _first_ argument to `call`:
```rust ```rust
fn add(x) { // define function which uses 'this' fn add(x) { // define function which uses 'this'
@ -167,3 +167,52 @@ Beware that this only works for _method-call_ style. Normal function-call style
the `this` pointer (for syntactic reasons). the `this` pointer (for syntactic reasons).
Therefore, obviously, binding the `this` pointer is unsupported under [`no_object`]. Therefore, obviously, binding the `this` pointer is unsupported under [`no_object`].
Call a Function Pointer in Rust
------------------------------
It is completely normal to register a Rust function with an [`Engine`] that takes parameters
whose types are function pointers. The Rust type in question is `rhai::FnPtr`.
A function pointer in Rhai is essentially syntactic sugar wrapping the _name_ of a function
to call in script. Therefore, the script's [`AST`] is required to call a function pointer,
as well as the entire _execution context_ that the script is running in.
For a rust function taking a function pointer as parameter, the [Low-Level API](../rust/register-raw.md)
must be used to register the function.
Essentially, use the low-level `Engine::register_raw_fn` method to register the function.
`FnPtr::call_dynamic` is used to actually call the function pointer, passing to it the
current scripting [`Engine`], collection of script-defined functions, the `this` pointer,
and other necessary arguments.
```rust
use rhai::{Engine, Module, Dynamic, FnPtr};
let mut engine = Engine::new();
// Define Rust function in required low-level API signature
fn call_fn_ptr_with_value(engine: &Engine, lib: &Module, args: &mut [&mut Dynamic])
-> Result<Dynamic, Box<EvalAltResult>>
{
// 'args' is guaranteed to contain enough arguments of the correct types
let fp = std::mem::take(args[1]).cast::<FnPtr>(); // 2nd argument - function pointer
let value = args[2].clone(); // 3rd argument - function argument
let this_ptr = args.get_mut(0).unwrap(); // 1st argument - this pointer
// Use 'FnPtr::call_dynamic' to call the function pointer.
// Beware, private script-defined functions will not be found.
fp.call_dynamic(engine, lib, Some(this_ptr), [value])
}
// Register a Rust function using the low-level API
engine.register_raw_fn("super_call",
&[ // parameter types
std::any::TypeId::of::<i64>(),
std::any::TypeId::of::<FnPtr>(),
std::any::TypeId::of::<i64>()
],
call_fn_ptr_with_value
);
```

View File

@ -59,7 +59,8 @@ Function Signature
The function signature passed to `Engine::register_raw_fn` takes the following form: The function signature passed to `Engine::register_raw_fn` takes the following form:
> `Fn(engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]) -> Result<T, Box<EvalAltResult>> + 'static` > `Fn(engine: &Engine, lib: &Module, args: &mut [&mut Dynamic])`
> `-> Result<T, Box<EvalAltResult>> + 'static`
where: where:
@ -117,7 +118,7 @@ engine.register_raw_fn(
std::any::TypeId::of::<FnPtr>(), std::any::TypeId::of::<FnPtr>(),
std::any::TypeId::of::<i64>(), std::any::TypeId::of::<i64>(),
], ],
move |engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]| { |engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]| {
// 'args' is guaranteed to contain enough arguments of the correct types // 'args' is guaranteed to contain enough arguments of the correct types
let fp = std::mem::take(args[1]).cast::<FnPtr>(); // 2nd argument - function pointer let fp = std::mem::take(args[1]).cast::<FnPtr>(); // 2nd argument - function pointer