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).
|
* Re-entrant scripting engine can be made `Send + Sync` (via the `sync` feature).
|
||||||
* [Function overloading](https://schungx.github.io/rhai/language/overload.html).
|
* [Function overloading](https://schungx.github.io/rhai/language/overload.html).
|
||||||
* [Operator overloading](https://schungx.github.io/rhai/rust/operators.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).
|
* 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).
|
* 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).
|
* 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:
|
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.
|
* Anonymous functions (in Rust closure syntax). Simplifies creation of single-use ad-hoc functions.
|
||||||
* Currying of function pointers.
|
* Currying of function pointers.
|
||||||
* Closures - auto-currying of anonymous functions to capture shared variables from the external scope.
|
* 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.
|
* Capturing call scope via `func!(...)` syntax.
|
||||||
|
|
||||||
New features
|
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`]).
|
([`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.
|
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.
|
Disable script-defined functions ([`no_function`]) and possibly closures ([`no_closure`]) when the features are not needed.
|
||||||
Both of these have little code size savings.
|
Both of these have some code size savings but not much.
|
||||||
|
|
||||||
|
|
||||||
Use a Raw [`Engine`]
|
Use a Raw [`Engine`]
|
||||||
|
@ -3,11 +3,12 @@ Performance Build
|
|||||||
|
|
||||||
{{#include ../../links.md}}
|
{{#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
|
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
|
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
|
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.
|
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.
|
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
|
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`.
|
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`)
|
Therefore, functions taking `String` parameters should use `ImmutableString` or `&str` (which maps to `ImmutableString`)
|
||||||
for the best performance with Rhai.
|
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 |
|
| [`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 |
|
| [`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 |
|
| [`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 |
|
| [`op1.rhai`]({{repoTree}}/scripts/op1.rhai) | Just simple addition |
|
||||||
| [`op2.rhai`]({{repoTree}}/scripts/op2.rhai) | Simple addition and multiplication |
|
| [`op2.rhai`]({{repoTree}}/scripts/op2.rhai) | Simple addition and multiplication |
|
||||||
| [`op3.rhai`]({{repoTree}}/scripts/op3.rhai) | Change evaluation order with parenthesis |
|
| [`op3.rhai`]({{repoTree}}/scripts/op3.rhai) | Change evaluation order with parenthesis |
|
||||||
|
@ -1,24 +1,19 @@
|
|||||||
// This script simulates object-oriented programming (OOP) techniques
|
// This script simulates object-oriented programming (OOP) techniques using closures.
|
||||||
// using function pointers (Fn) and object maps.
|
|
||||||
|
// External variable that will be captured.
|
||||||
|
let last_value = ();
|
||||||
|
|
||||||
// Define object
|
// Define object
|
||||||
let obj1 = #{
|
let obj1 = #{
|
||||||
_data: 42, // data field
|
_data: 42, // data field
|
||||||
get_data: Fn("getData"), // property getter
|
get_data: || this._data, // property getter
|
||||||
action: Fn("action"), // method
|
action: || print("Data=" + this._data), // method
|
||||||
update: Fn("update1") // property setter
|
update: |x| { // property setter
|
||||||
};
|
|
||||||
|
|
||||||
fn getData() {
|
|
||||||
this._data
|
|
||||||
}
|
|
||||||
fn action() {
|
|
||||||
print("Data=" + this._data);
|
|
||||||
}
|
|
||||||
fn update1(x) {
|
|
||||||
this._data = x;
|
this._data = x;
|
||||||
|
last_value = this._data; // capture 'last_value'
|
||||||
this.action();
|
this.action();
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if obj1.get_data() > 0 { // property access
|
if obj1.get_data() > 0 { // property access
|
||||||
obj1.update(123); // call method
|
obj1.update(123); // call method
|
||||||
@ -29,17 +24,18 @@ if obj1.get_data() > 0 { // property access
|
|||||||
// Define another object based on the first object
|
// Define another object based on the first object
|
||||||
let obj2 = #{
|
let obj2 = #{
|
||||||
_data: 0, // data field - new value
|
_data: 0, // data field - new value
|
||||||
update: Fn("update2") // property setter - another function
|
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
|
if obj2.get_data() > 0 { // property access
|
||||||
obj2.update(0); // call method
|
print("we have another problem here");
|
||||||
} else {
|
} 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
|
||||||
///
|
///
|
||||||
/// Panics under the `no_closure` feature.
|
/// Panics under the `no_closure` feature.
|
||||||
|
#[inline(always)]
|
||||||
pub fn into_shared(self) -> Self {
|
pub fn into_shared(self) -> Self {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
@ -833,7 +834,6 @@ impl Dynamic {
|
|||||||
let data = cell.read().unwrap();
|
let data = cell.read().unwrap();
|
||||||
|
|
||||||
let type_id = (*data).type_id();
|
let type_id = (*data).type_id();
|
||||||
println!("Type = {}", (*data).type_name());
|
|
||||||
|
|
||||||
if type_id != TypeId::of::<T>() && TypeId::of::<Dynamic>() != TypeId::of::<T>() {
|
if type_id != TypeId::of::<T>() && TypeId::of::<Dynamic>() != TypeId::of::<T>() {
|
||||||
None
|
None
|
||||||
|
@ -705,12 +705,25 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
if let Some(map) = obj.read_lock::<Map>() {
|
if let Some(map) = obj.read_lock::<Map>() {
|
||||||
if let Some(val) = map.get(_fn_name) {
|
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
|
// Remap the function name
|
||||||
redirected = f.get_fn_name().clone();
|
redirected = fn_ptr.get_fn_name().clone();
|
||||||
_fn_name = &redirected;
|
_fn_name = &redirected;
|
||||||
// Recalculate the hash based on the new function name
|
// Add curried arguments
|
||||||
_hash = calc_fn_hash(empty(), _fn_name, idx.len(), empty());
|
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)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use crate::any::{Dynamic, Variant, DynamicWriteLock};
|
use crate::any::{Dynamic, DynamicWriteLock, Variant};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync};
|
use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync};
|
||||||
use crate::module::Module;
|
use crate::module::Module;
|
||||||
@ -11,12 +11,7 @@ use crate::r#unsafe::unsafe_cast_box;
|
|||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::utils::ImmutableString;
|
use crate::utils::ImmutableString;
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{any::TypeId, boxed::Box, mem, string::String};
|
||||||
any::TypeId,
|
|
||||||
boxed::Box,
|
|
||||||
mem,
|
|
||||||
string::String,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Trait to register custom functions with the `Engine`.
|
/// Trait to register custom functions with the `Engine`.
|
||||||
pub trait RegisterFn<FN, ARGS, RET> {
|
pub trait RegisterFn<FN, ARGS, RET> {
|
||||||
|
Loading…
Reference in New Issue
Block a user