Qore Programming Language Reference Manual  1.12.1
Hashdecl: Type-Safe Hash Declarations

Type-Safe Hashes Overview

The hashdecl keyword allows for type-safe hashes to be declared; they can then be instantiated with the new operator, the cast<> operator, or with variable implicit construction.

hashdecl Declaration Syntax

hashdecl hashdecl_identifier {
    member_type member_name [= initialization_expression];
    [...]
}

At least one member must be defined; it's not possible to declare an empty type-safe hash.

Example
hashdecl MyHash {
int i = 1;
string code = "other";
}
Note
  • a hashdecl may not have the name "auto", this name has a special meaning in complex types
  • Each Qore type has a "pseudo-class" associated with it; for hashes the type is <hash>; methods from the data type's "pseudo-class" can be run on any value of that type.

Type-Safe Hash Creation

When type-safe hashes are created, the hash is automatically populated with the values given by the initialization expressions in the hashdecl declaration (if any).

It is possible to override these declarations by passing a hash to be used for initialization; this can be passed the single optional argument to the type-safe hash initialization as in the following examples:

Examples
# type-safe hash declaration
hashdecl Container {
int i = 1;
}
# immediate value with implicit construction: default values are assigned from the declaration
auto ah1 = hash<Container>{};
# immediate value with implicit construction: default values are assigned from the declaration
auto ah2 = <Container>{};
# immediate value with implicit construction: overrides the "i" member's initial value
auto ah3 = <Container>{"i": 2};
# implicit construction: default values are assigned from the declaration
hash<Container> h1();
# implicit construction: overrides the "i" member's initial value
hash<Container> h2(("i": 2));
# "new" construction: default values are assigned from the declaration
hash<Container> h3 = new hash<Container>();
# "new" construction: overrides the "i" member's initial value
hash<Container> h4 = new hash<Container>(("i": 2));

In such cases, the initialization expression for the members being overridden is never executed and the supplied value is used instead.

Note that it's illegal to assign a value to an unassigned lvalue declared as a typed hash; in such cases a HASHDECL-IMPLICIT-CONSTRUCTION-ERROR exception is thrown as in the following example:

hash<Container> c;
# the following line will result in a HASHDECL-IMPLICIT-CONSTRUCTION-ERROR exception being thrown
c.i = 2;

To address this, ensure that your typed hash lvalues are always initialized before assignment as in the following example:

hash<Container> c();
c.i = 2;
See also

Type-Safe Hash Type Compatibility

Type-safe hashes can be assigned to any variable that accepts an untyped hash, however variables declared as a particular type-safe hash (ex: hash<MyHash>) can only be assigned values of the given type; use the cast<> operator to convert untyped hashes or type-safe hashes of another type to the target type for assignment.

Example:
hashdecl Container1 {
int i = 1;
string str = "value";
}
hashdecl Container2 {
int i = 2;
bool check = False;
}
hash<Container1> c1();
# afterwards c2 will be: {i: 1, check: False}
hash<Container2> c2 = cast<hash<Container2>>(cast<hash>(c1 - "str"));
hash h = ("i": 3, "other": "x");
# afterwards c3 will be: {i: 3, check: False}
hash<Container2> c3 = cast<hash<Container2>>(cast<hash>(h - "other"));
See also
cast<>

Comparison of Type-Safe Hashes and Objects

Type-safe hashes are similar to objects in that they have members, but there are some important differences as follows:

  • type-safe hashes are always passed by value whereas objects are always passed by reference
  • object classes can have methods, type-safe hashes cannot
  • object classes allow for class members to have access restrictions; type-safe hashes are made up exclusively of public members
  • object classes can participate in a hierarchy, type-safe hashes cannot