Page MenuHomeFeedback Tracker

[Feature Request] Extension for new HashMapObject
New, NormalPublic

Description

Disclaimer Every feature below (I think) can be realised in plain SQF functions, but it would be convenient for code, that heavily depends on new Hash Map Object, to write it "in engine".

Special keys

Many of these features are inspired by Lua metatables. While new HashMap Object have slightly different approach, we still can bring some of Lua metatables features to it.

"#*key"

Methods for controlling reads from and writes to hashmap like Lua metamethods:

  1. "#key" (Lua __index) - getting value of non-existing key:
    • Passed parameters: ["_key"].
    • getOrDefault returns default value only if this method returns nil.
  1. "#newkey" (Lua __newindex) - inserting a new key:
    • Passed parameters: ["_key", "_value"].
    • If this method is present, override the default insert behavior (new setraw command - more information below).
    • If this method is present, call it even if the "sealed" flag is set or finalized.
  1. "#callkey" - calling non-existing method:
    • Passed parameters: ["_key", "_params"].

New commands *raw

New commands: setRaw, getRaw, getOrDefaultRaw and callRaw - that work same as their regular counterparts, but don't call "#*key" methods and still follow "sealed" flag and finalization.

Like Lua rawget and rawset

Finalization of special keys only

This can be done through any of:

  • New special flag "finalSpecial".
  • or special method "#specialkey" that will override any write for special key.

Like Lua __metatable that allow disabling of modification metamethods.

Overriding operators

KeySQFParameters
"#add"HASHMAP + ANYTHING["_rhs"]
"#radd"ANYTHING + HASHMAP["_lhs"]
"#sub"HASHMAP - ANYTHING["_rhs"]
"#rsub"ANYTHING - HASHMAP["_lhs"]
"#mul"HASHMAP * ANYTHING["_rhs"]
"#rmul"ANYTHING * HASHMAP["_lhs"]
"#div"HASHMAP / ANYTHING["_rhs"]
"#rdiv"ANYTHING / HASHMAP["_lhs"]
"#mod"HASHMAP % ANYTHING, HASHMAP mod ANYTHING["_rhs"]
"#rmod"ANYTHING % HASHMAP, ANYTHING mod HASHMAP["_lhs"]
"#pow"HASHMAP ^ ANYTHING["_rhs"]
"#rpow"ANYTHING ^ HASHMAP["_lhs"]
"#eq"HASHMAP == HASHMAP (Same for: isEqualTo, in, find and etc.)["_other"]
"#ne"HASHMAP != HASHMAP (Same for: isNotEqualTo)["_other"]
"#lt"HASHMAP < HASHMAP["_rhs"]
"#le"HASHMAP <= HASHMAP["_rhs"]
"#gt"HASHMAP > HASHMAP["_rhs"]
"#ge"HASHMAP >= HASHMAP["_rhs"]
"#neg"-HASHMAP[]
"#not"!HASHMAP, not HASHMAP[]
"#shiftr"HASHMAP >> ANYTHING["_rhs"]
"#rshiftr"ANYTHING >> HASHMAP["_lhs"]
"#select"HASHMAP select ANYTHING, HASHMAP # ANYTHING["_rhs"]
"#count"count HASHMAP[]
"#forEach"CODE forEach HASHMAP["_code"]
"#call"call HASHMAP, ANYTHING call HASHMAPlike normal call

For binary operators where both operands are hashmaps with defined apropriate special method prioritize method of left operand.

Type checking

Everything below allow to write code like:

params [["_value", false, [true, 0, HashMapType1, HashMapType2]]];

switch (typeName _value) do {
    case (typeName true) : {...};
    case (typeName 0) : {...};
    case (typeName HashMapType1) : {...};
    case (typeName HashMapType2) : {...};
};

if (_value isEqualType HashMapType1) then {...};
if (_value isEqualType HashMapType2) then {...};

typeName HASHMAP

Change typeName to returns "#type" for hashmaps if it's defined:

typeName HashMap;
// Same as
if not ("#type" in HashMap) exitWith {"HASHMAP"};
HashMap get "#type";

isEqualType*

For each variant of isEqualType*: if comparing two hashmaps with defined "#type" in rhs hashmap then they are equal by type when lhs hashmap is equal to rhs hashmap by reference or any of hashmap from lhs "#base" are equal by reference to rhs hashmap:

HashMap1 isEqualType HashMap2;
// Same as
if not ("#type" in HashMap2) exitWith {true};
if (HashMap1 isEqualRef HashMap2) exitWith {true};
HashMap1 getOrDefault ["#base", []] findIf {_x isEqualRef HashMap2} != -1;

param*

For hashmaps in expectedDataTypes input values are compared by algorithm described above:

params [["_value", false, [true, 0, HashMapType1, HashMapType2]]];

Misc

createHashMap HASHMAP

Returns copy of hashmap object as simple hashmap without any special methods.

Additional: if finilization of special keys or entire hashmap is present then this must fail.

Details

Severity
Feature
Resolution
Open
Reproducibility
N/A
Operating System
Windows 11 x64
Category
Feature Request

Event Timeline

Addition #1 - Type checking

To avoid braking of backward compatibility
we can introduce new commands with slightly
different name, but same behaviour as normal
counterparts, except for hasmaps:

  • typeName -> typeNameOO
  • isEqualType* -> isEqualTypeOO*

For param* we can move from
expectedDataTypes to expectedArrayCount that may contains:

  • No hashmap - all hashmaps are allowed
  • Non-typed hashmap - only non-typed hashmaps are allowed, typed hashmaps are forbiden
  • Typed hashmap(-s) - only hashmap of these type(-s) are allowed, non-typed hashmaps are forbidden
  • Both typed and non-typed hashmap - any non-typed hashmap and hashmaps of specified type(-s) are allowed

Addition #2 - Finalization of special keys only

To make it clearer and more useful I rewritten this part of proposal with more explanations.

  1. First need to declare that engine will never use special keys of specific format for example: "#_*":
createHashMapObject [[
    ["NonSpecialKey", ANYRHING],
    ["#EngineSpecialKey", ANYTHING],
    ["#_UserSpecialKey", ANYTHING]
]];
  1. Introduce new flag "final" that denied any special key read, write and execution from anywhere:
private _object = createHashMapObject [[
    ["#flags", ["final"]]
]];

// ERROR: access to finalized special key denied
_object get "#SpecialKey";
_object set ["#SpecialKey", ANYTHING];
_object call ["#SpecialKey"];

// Same for object methods:
// OK: new non special key defined
_object set ["Method", {
    // ERROR: access to finalized special key denied
    _self get "#SpecialKey";
    _self set ["#SpecialKey", ANYTHING];
    _self call ["#SpecialKey"];
}];
  1. But
    • not for special methods:
createHashMapObject [[
    ["#flags", ["final"]],
    ["#SpecialMethod", {
        // OK: access from special method
        _self get "#SpecialKey";
        _self set ["#SpecialKey", ANYTHING];
        _self call ["#SpecialKey"];
    }]
]];
  • And not for finalized non special methods, that inherited from class definition:
private _object = createHashMapObject [[
    ["#flags", ["final"]],
    ["NonFinalizedMethod", {
        // ERROR: access to finalized special key denied
        _self get "#SpecialKey";
        _self set ["#SpecialKey", ANYTHING];
        _self call ["#SpecialKey"];
    }],
    ["FinalizedMethod", compileFinal {
        // OK: access from finalized inherited method
        _self get "#SpecialKey";
        _self set ["#SpecialKey", ANYTHING];
        _self call ["#SpecialKey"];
    }]
]];

_object set ["FinalizedNonInheritedMethod", compileFinal {
    // ERROR: access to finalized special key denied
    _self get "#SpecialKey";
    _self set ["#SpecialKey", ANYTHING];
    _self call ["#SpecialKey"];
}];
  • If this flag presented createHashMap HASHMAP should fail:
private _object createHashMapObject [[
    ["#flags", ["final"]]
]];

// ERROR: attempt to remove finalized special key
private _hashmap = createHashMap _object;
  • and this class shouldn't be used in "#base":
// OK
MyFinalClass = createHashMapObject [[
    ["#flags", ["final"]]
]];

// ERROR: attempt to derive from finalized class
MyDerivedClass = createHashMapObject [[
    ["#base", MyFinalClass]
]];

// OK: just create new object of MyFinalClass
MyAnotherClass = createHashmapObject [MyFinalClass];
dedmen set Ref Ticket to AIII-55997.Sep 19 2023, 1:51 PM