Merge pull request #207 from schungx/master
Fix currying in object map closure method calls.
This commit is contained in:
commit
dab85c9070
@ -31,7 +31,7 @@ Standard features
|
||||
* Re-entrant scripting engine can be made `Send + Sync` (via the `sync` feature).
|
||||
* [Function overloading](https://schungx.github.io/rhai/language/overload.html).
|
||||
* [Operator overloading](https://schungx.github.io/rhai/rust/operators.html).
|
||||
* Dynamic dispatch via [function pointers](https://schungx.github.io/rhai/language/fn-ptr.html) with additional support for [currying](https://schungx.github.io/rhai/language/fn-curry.html).
|
||||
* Dynamic dispatch via [function pointers](https://schungx.github.io/rhai/language/fn-ptr.html) with additional support for [currying](https://schungx.github.io/rhai/language/fn-curry.html) and [closures](https://schungx.github.io/rhai/language/fn-closure.html).
|
||||
* Some support for [object-oriented programming (OOP)](https://schungx.github.io/rhai/language/oop.html).
|
||||
* Organize code base with dynamically-loadable [modules](https://schungx.github.io/rhai/language/modules.html).
|
||||
* Serialization/deserialization support via [serde](https://crates.io/crates/serde) (requires the `serde` feature).
|
||||
|
@ -6,10 +6,10 @@ Version 0.18.0
|
||||
|
||||
This version adds:
|
||||
|
||||
* Binding the `this` pointer in a function pointer `call`.
|
||||
* Anonymous functions (in Rust closure syntax). Simplifies creation of single-use ad-hoc functions.
|
||||
* Currying of function pointers.
|
||||
* Closures - auto-currying of anonymous functions to capture shared variables from the external scope.
|
||||
* Binding the `this` pointer in a function pointer `call`.
|
||||
* Capturing call scope via `func!(...)` syntax.
|
||||
|
||||
New features
|
||||
|
@ -38,8 +38,8 @@ Omitting arrays ([`no_index`]) yields the most code-size savings, followed by fl
|
||||
([`no_float`]), checked arithmetic/script resource limits ([`unchecked`]) and finally object maps and custom types ([`no_object`]).
|
||||
|
||||
Where the usage scenario does not call for loading externally-defined modules, use [`no_module`] to save some bytes.
|
||||
Disable script-defined functions ([`no_function`]) when the feature is not needed.
|
||||
Both of these have little code size savings.
|
||||
Disable script-defined functions ([`no_function`]) and possibly closures ([`no_closure`]) when the features are not needed.
|
||||
Both of these have some code size savings but not much.
|
||||
|
||||
|
||||
Use a Raw [`Engine`]
|
||||
|
@ -3,11 +3,12 @@ Performance Build
|
||||
|
||||
{{#include ../../links.md}}
|
||||
|
||||
Some features are for performance. For example, using [`only_i32`] or [`only_i64`] disables all other integer types (such as `u16`).
|
||||
|
||||
|
||||
Use Only One Integer Type
|
||||
------------------------
|
||||
|
||||
Some features are for performance. For example, using [`only_i32`] or [`only_i64`] disables all other integer types (such as `u16`).
|
||||
|
||||
If only a single integer type is needed in scripts - most of the time this is the case - it is best to avoid registering
|
||||
lots of functions related to other integer types that will never be used. As a result, [`Engine`] creation will be faster
|
||||
because fewer functions need to be loaded.
|
||||
@ -22,8 +23,8 @@ On 64-bit targets this may not gain much, but on some 32-bit targets this improv
|
||||
requiring more CPU cycles to complete.
|
||||
|
||||
|
||||
Minimize Size of [`Dynamic`]
|
||||
---------------------------
|
||||
Minimize Size of `Dynamic`
|
||||
-------------------------
|
||||
|
||||
Turning on [`no_float`], and [`only_i32`] makes the key [`Dynamic`] data type only 8 bytes small on 32-bit targets
|
||||
while normally it can be up to 16 bytes (e.g. on x86/x64 CPU's) in order to hold an `i64` or `f64`.
|
||||
@ -42,3 +43,15 @@ It is cheap to clone, but expensive to modify (a new copy of the string must be
|
||||
|
||||
Therefore, functions taking `String` parameters should use `ImmutableString` or `&str` (which maps to `ImmutableString`)
|
||||
for the best performance with Rhai.
|
||||
|
||||
|
||||
Disable Closures
|
||||
----------------
|
||||
|
||||
Support for [closures], including capturing shared values, adds material overhead to script evaluation.
|
||||
|
||||
This is because every data access must be checked whether it is a shared value, and if so, take a read
|
||||
or write lock before reading it.
|
||||
|
||||
Use [`no_closure`] to disable closure and capturing support and optimize the hot path
|
||||
because there is no need to take locks for shared data.
|
||||
|
@ -20,7 +20,7 @@ There are also a number of examples scripts that showcase Rhai's features, all i
|
||||
| [`function_decl3.rhai`]({{repoTree}}/scripts/function_decl3.rhai) | A [function] with many parameters |
|
||||
| [`if1.rhai`]({{repoTree}}/scripts/if1.rhai) | [`if`]({{rootUrl}}/language/if.md) example |
|
||||
| [`loop.rhai`]({{repoTree}}/scripts/loop.rhai) | Count-down [`loop`]({{rootUrl}}/language/loop.md) in Rhai, emulating a `do` .. `while` loop |
|
||||
| [`oop.rhai`]({{repoTree}}/scripts/oop.rhai) | Simulate [object-oriented programming (OOP)][OOP] |
|
||||
| [`oop.rhai`]({{repoTree}}/scripts/oop.rhai) | Simulate [object-oriented programming (OOP)][OOP] with [closures] |
|
||||
| [`op1.rhai`]({{repoTree}}/scripts/op1.rhai) | Just simple addition |
|
||||
| [`op2.rhai`]({{repoTree}}/scripts/op2.rhai) | Simple addition and multiplication |
|
||||
| [`op3.rhai`]({{repoTree}}/scripts/op3.rhai) | Change evaluation order with parenthesis |
|
||||
|
@ -1,45 +1,41 @@
|
||||
// This script simulates object-oriented programming (OOP) techniques
|
||||
// using function pointers (Fn) and object maps.
|
||||
// This script simulates object-oriented programming (OOP) techniques using closures.
|
||||
|
||||
// External variable that will be captured.
|
||||
let last_value = ();
|
||||
|
||||
// Define object
|
||||
let obj1 = #{
|
||||
_data: 42, // data field
|
||||
get_data: Fn("getData"), // property getter
|
||||
action: Fn("action"), // method
|
||||
update: Fn("update1") // property setter
|
||||
_data: 42, // data field
|
||||
get_data: || this._data, // property getter
|
||||
action: || print("Data=" + this._data), // method
|
||||
update: |x| { // property setter
|
||||
this._data = x;
|
||||
last_value = this._data; // capture 'last_value'
|
||||
this.action();
|
||||
}
|
||||
};
|
||||
|
||||
fn getData() {
|
||||
this._data
|
||||
}
|
||||
fn action() {
|
||||
print("Data=" + this._data);
|
||||
}
|
||||
fn update1(x) {
|
||||
this._data = x;
|
||||
this.action();
|
||||
}
|
||||
|
||||
if obj1.get_data() > 0 { // property access
|
||||
obj1.update(123); // call method
|
||||
if obj1.get_data() > 0 { // property access
|
||||
obj1.update(123); // call method
|
||||
} else {
|
||||
print("we have a problem here");
|
||||
}
|
||||
|
||||
// Define another object based on the first object
|
||||
let obj2 = #{
|
||||
_data: 0, // data field - new value
|
||||
update: Fn("update2") // property setter - another function
|
||||
_data: 0, // data field - new value
|
||||
update: |x| { // property setter - another function
|
||||
this._data = x * 2;
|
||||
last_value = this._data; // capture 'last_value'
|
||||
this.action();
|
||||
}
|
||||
};
|
||||
obj2.fill_with(obj1); // add all other fields from obj1
|
||||
obj2.fill_with(obj1); // add all other fields from obj1
|
||||
|
||||
fn update2(x) {
|
||||
this._data = x * 2;
|
||||
this.action();
|
||||
}
|
||||
|
||||
if obj2.get_data() > 0 { // property access
|
||||
obj2.update(0); // call method
|
||||
if obj2.get_data() > 0 { // property access
|
||||
print("we have another problem here");
|
||||
} else {
|
||||
obj2.update(42); // call method
|
||||
obj2.update(42); // call method
|
||||
}
|
||||
|
||||
print("Should be 84: " + last_value);
|
||||
|
@ -600,6 +600,7 @@ impl Dynamic {
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics under the `no_closure` feature.
|
||||
#[inline(always)]
|
||||
pub fn into_shared(self) -> Self {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
return match self.0 {
|
||||
@ -833,7 +834,6 @@ impl Dynamic {
|
||||
let data = cell.read().unwrap();
|
||||
|
||||
let type_id = (*data).type_id();
|
||||
println!("Type = {}", (*data).type_name());
|
||||
|
||||
if type_id != TypeId::of::<T>() && TypeId::of::<Dynamic>() != TypeId::of::<T>() {
|
||||
None
|
||||
|
@ -705,12 +705,25 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
if let Some(map) = obj.read_lock::<Map>() {
|
||||
if let Some(val) = map.get(_fn_name) {
|
||||
if let Some(f) = val.read_lock::<FnPtr>() {
|
||||
if let Some(fn_ptr) = val.read_lock::<FnPtr>() {
|
||||
// Remap the function name
|
||||
redirected = f.get_fn_name().clone();
|
||||
redirected = fn_ptr.get_fn_name().clone();
|
||||
_fn_name = &redirected;
|
||||
// Recalculate the hash based on the new function name
|
||||
_hash = calc_fn_hash(empty(), _fn_name, idx.len(), empty());
|
||||
// Add curried arguments
|
||||
if !fn_ptr.curry().is_empty() {
|
||||
fn_ptr
|
||||
.curry()
|
||||
.iter()
|
||||
.cloned()
|
||||
.enumerate()
|
||||
.for_each(|(i, v)| idx.insert(i, v));
|
||||
}
|
||||
// Recalculate the hash based on the new function name and new arguments
|
||||
_hash = if native {
|
||||
0
|
||||
} else {
|
||||
calc_fn_hash(empty(), _fn_name, idx.len(), empty())
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::any::{Dynamic, Variant, DynamicWriteLock};
|
||||
use crate::any::{Dynamic, DynamicWriteLock, Variant};
|
||||
use crate::engine::Engine;
|
||||
use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync};
|
||||
use crate::module::Module;
|
||||
@ -11,12 +11,7 @@ use crate::r#unsafe::unsafe_cast_box;
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::utils::ImmutableString;
|
||||
|
||||
use crate::stdlib::{
|
||||
any::TypeId,
|
||||
boxed::Box,
|
||||
mem,
|
||||
string::String,
|
||||
};
|
||||
use crate::stdlib::{any::TypeId, boxed::Box, mem, string::String};
|
||||
|
||||
/// Trait to register custom functions with the `Engine`.
|
||||
pub trait RegisterFn<FN, ARGS, RET> {
|
||||
|
Loading…
Reference in New Issue
Block a user