/** @page container_data_types Container Data Types

    @tableofcontents

    %Qore supports three types of container types (see also @ref basic_data_types and @ref code_data_types):
    - @ref list "lists"
    - @ref hash "hashes" (associative arrays)
    - @ref object "objects" (see @ref qore_classes for more information)

    These container types can be combined to make arbitrarily complex data structures. The data type of any element can be any basic type or another aggregate type. The types do not have to be uniform in one container structure.

    <hr>
    @section list List

    @par Description:
    Lists (or arrays) are simply ordered containers of values. A list element can be any %Qore type (even another list, @ref hash "hash", or @ref object "object").\n\n
    Lists are specified by giving expressions separated by commas as follows:\n
    @code{.py}
list = (expression, expression [, expression ...]);
    @endcode \n
    Here is a concrete example:\n
    @code{.py}
list list = (
    1,
    2,
    "three",
    4.0,
    5e20n,
    6,
    2001-01-15Z,
);
    @endcode \n
    Trailing commas can be left on the end of a list (or a @ref hash "hash"), making it easier to insert and remove elements at the end of a multi-line list.\n\n
    List elements are dereferenced using square brackets: \c "[" and \c "]". The first element in a list has index zero.\n\n
    <b>Example:</b>
    @code{.py}
element3 = list[2];
    @endcode \n
    The following operators perform special processing on lists:
    - @ref elements "elements"
    - @ref shift "shift"
    - @ref unshift "unshift"
    - @ref push "push"
    - @ref pop "pop"
    - @ref splice "splice"
    - @ref list_element_operator "[]"
    - @ref plus_operator "+"
    - @ref plus_equals_operator "+="
    - @ref map "map"
    - @ref foldl "foldl"
    - @ref foldr "foldr"
    - @ref select "select" \n

    @par Immediate Value Example:
    @verbatim (1, "two", 3.0)@endverbatim
    Gives an empty list (note that <tt>{}</tt> gives an empty hash):
    @verbatim ()@endverbatim

    @par Pseudo Class for Type List:
    @ref Qore::zzz8listzzz9

    @par Type Code:
    @ref Qore::NT_LIST

    @par Type Name:
    \c "list"

    The list type supports a complex element type specification as well, however "list" lvalues without a
    complex element type specification will strip the complex type when assigned as in the following example:
    @code{.py}
list l0 = (2, 3);
# prints "list"
printf("%y\n", l0.fullType());
list<auto> l1 = (2, 3);
# prints "list<int>"
printf("%y\n", l1.fullType());
    @endcode

    @anchor list_slicing
    @par List slicing:
    Lists can be "sliced" by dereferencing them using the @ref list_element_operator "[] operator" with a range or a list, as in the following examples:\n
    @code{.py}
list<int> l = (1, 2, 3, 4);

# Creates a new list with the first two elements of the original list
list l1a = l[0..1];
list l1b = l[0,1];
list l1c = l[..1];
printf("slice 1a: %y\n", l1a);
printf("slice 1b: %y\n", l1b);
printf("slice 1c: %y\n", l1c);

# Creates a new list with the last two elements of the original list in reverse order
list l2a = l[3..2];
list l2b = l[3,2];
printf("slice 2a: %y\n", l2a);
printf("slice 2b: %y\n", l2b);

# Takes a slice of different parts of the list
list l3 = l[3,2..1,0];
printf("slice 3: %y\n", l3);

# -------------------------
# the output is as follows:

slice 1a: [1, 2]
slice 1b: [1, 2]
slice 1c: [1, 2]
slice 2a: [4, 3]
slice 2b: [4, 3]
slice 3: [4, 3, 2, 1];
    @endcode \n

    When making a slice of a list with a list of elements and referring to an element index that does not exist,
    @ref nothing is returned for that list element in the resulting list as in the following example:
    @code{.py}
list<int> l = (1, 2, 3, 4);
printf("%y\n", l[2,4,6]);

# -------------------------
# the output is as follows:

[3, null, null]
    @endcode

    @see
    - @ref list_type, @ref softlist_type
    - @ref list_functions

    @par Complex Type Support for Lists
    The \c "list" type also supports an optional type argument which allows the value type of list elements to be declared; the following example demonstrates a list declaration with a specific value type:\n
    @code{.py}
list<int> l(2, 3);
    @endcode \n
    This type is supported at parse-time and at runtime; to convert such values to an untyped list, assign it
    to a @ref list_type "list" lvalue, use @ref cast "cast<list>(...)" on the value, or call the @ref Qore::list() "list()" function on the value.  Each of these options can be used to convert a type-safe list to an untyped list.

    A special type argument, \c "auto", allows for the lvalue to maintain the complex list type as in the following example:
    @code{.py}
list l0 = (2, 3);
# prints "list"
printf("%y\n", l0.fullType());
list<auto> l1 = (2, 3);
# prints "list<int>"
printf("%y\n", l1.fullType());
    @endcode

    @see @ref list_complex_type

    @note
    - Trailing commas can be left on the end of a list (or a @ref hash "hash"), making it easier to insert and remove elements at the end of a multi-line list.
    - List elements are dereferenced using square brackets: \c "[" and \c "]". The first element in a list has index zero.
    - Dereferencing an invalid element (past the end of the list or with a negative number) will return @ref nothing
    - Use <list>::iterator() as an easy way to get a list iterator object for the current list

    <hr>
    @section hash Hash

    @par Description:
    Hashes are containers that associate values to a string key and also preserve key order for consistent data
    serialization/deserialization. If anything else than a string is used as a key, the value is one-way converted to
    a string and the string value is used as a key. This can sometimes have unexpected results and therefore only string
    keys are recommended. Hash key lookups are case-sensitive and use a hashing algorithm that in the worst case should
    provide <i>O(ln(n))</i> complexity to execute; in the best case finding a particular key in a hash is executed in
    constant time (ie <i>O(1)</i> complexity).\n\n

    Hashes are specified using the following syntax:\n
    @code{.py}
hash h = {
    key_expression: value_expression,
    key_expression: value_expression,
    ...,
};
    @endcode \n
    with a @ref hashdecl "typed hash", immediate values can look like:
    @code{.py}
# the following declarations are equivalent
# note that typed hashes are "constructed" with default key values; immediate values override the defaults
# with the following format, there can be no spaces between the brackets, the hashdecl identifier, and the open curly brace
auto th0 = <MyHash>{
    key_expression: value_expression,
    key_expression: value_expression,
    ...,
};
# with the following format, there can be no spaces between the "hash" string, the brackets, the hashdecl identifier, and the open curly brace
auto th1 = hash<MyHash>{
    key_expression: value_expression,
    key_expression: value_expression,
    ...,
};
auto th2 = cast<hash<MyHash>>({
    key_expression: value_expression,
    key_expression: value_expression,
    ...,
});
auto th3 = new <hash<MyHash>>({
    key_expression: value_expression,
    key_expression: value_expression,
    ...,
});
    @endcode \n
    or alternatively, a simple non-typed immediate hash can be declared with parentheses:
    @code{.py}
hash h2 = (
    key_expression: value_expression,
    key_expression: value_expression,
    ...,
);
    @endcode \n
    Here is a concrete example: \n
    @code{.py}
hash hash = {
    "apple": 1 + 1,
    "pear": "string",
};
    @endcode \n
    Trailing commas are ignored (as with @ref list "lists") to allow for easier insertion and deletion of elements in source code.\n\n
    Hashes are dereferenced in one of two ways, either using curly brackets: \c "{" and \c "}", where any valid %Qore expression can be used, or using the dot "." hash member dereferencing operator, where literal strings can be used.\n\n
    @code{.py}
element3 = hash{"pe" + "ar"};
    @endcode \n
    Is equivalent to: \n
    @code{.py}
element3 = hash.pear;
    @endcode \n
    and: \n
    @code{.py}
element3 = hash."pear";
    @endcode \n
    and: \n
    @code{.py}
element3 = hash.("pe" + "ar");
    @endcode \n
    Hash members can have the names of keywords or names that are not valid identifiers, but in this case to dereference them you cannot use the dot operator with a literal string, otherwise a parse error will be raised. Use quotes around the member name when dereferencing hash members with the same name as a qore keyword or other name that is not a valid identifier as follows:\n
    @code{.py}
element3 = hash."keys";
element3 = hash{"this-element-1!"};
    @endcode \n
    A literal string after the dot "." hash member dereferencing operator must be a valid %Qore identifier; therefore if you want to use a hash key that's not a valid identifier, enclose the string in quotes.\n\n
    If you want to use the result of an expression to dereference the hash, then either the curly bracket syntax must be used or the expression must be enclosed in parentheses.\n\n
    In the case of using a literal string with the dot operator, keep in mind that the string is always interpreted as a literal string name of the member, even if there is a constant with the same name.  To use the value of a constant to dereference a hash, use curly brackets with the constant: ex: @code{.py} hash{MyConstant} @endcode
    Note that hash keys can also be given by constants (as long as the constant resolves to a string) when using curly brackets.\n

    @note using curly-bracket delimiters with an immediate hash with \c map results in the @ref hmap "hash version of the map operator")

    @par Hash slicing:
    Hashes can be "sliced" by dereferencing them using parenthesis syntax with more than one key, as in the following example:\n
    @code{.py}
hash h = {
    "a": 3,
    "b": 5,
    "x": "test",
    "y": 0,
    "text": "lorem ipsum dolor sit amet",
    "float": 2.71828
};

# Creates a new hash with key-value pairs "a" and "b" from the original hash.
hash nh = h.("a", "b");
printf("slice 1: %y\n", nh);

# Creates a new hash with key-value pairs "x", "y" and "float" from the original hash.
printf("slice 2: %y\n", h.("x", "y", "float"));

# Creates a new hash with a key-value pair "x" from the original hash.
printf("slice 3: %y\n", h.("x", "random"));

# Creates an empty hash because hash h does not contain keys "abc" or "xyz".
printf("slice 4: %y\n", h.("abc", "xyz"));

# -------------------------
# the output is as follows:

slice 1: {a: 3, b: 5}
slice 2: {x: "test", y: 0, float: 2.71828}
slice 3: {x: "test"}
slice 4: {}
    @endcode \n

    All keys matching with the original hash's key-value pairs will be included in the newly created hash. Keys which are not in the original hash will be silently ignored.

    @par Immediate Value Examples:
    @verbatim {"key1": 1, "key2": "two", get_key_3(): 3.141592653589793238462643383279502884195n}@endverbatim
    @verbatim <Container>{"i": 2}@endverbatim
    @verbatim hash<Container>{}@endverbatim
    Hashes can be declared with curly brackets (preferred) or parentheses:
    @verbatim ("key1": 1, "key2": "two", get_key_3(): 3.141592653589793238462643383279502884195n)@endverbatim
    Gives an empty hash (note that <tt>()</tt> gives an empty list):
    @verbatim hash h = {};@endverbatim

    @par Typed Hash Declarations
    The \c "hash" type supports a variant with pre-defined type-safe keys which can be specified with a single argument giving a @ref hashdecl "type-safe hashdecl" identifier in angle brackets after the \c "hash" type.  Furthermore, type-safe hashes can be declared using the @ref hashdecl "hashdecl" keyword; the following example demonstrates a type-safe hash declaration and then a variable restricted to this type:\n
    @code{.py}
# hashdecl typed hash declaration
hashdecl Container {
    int i = 1;
    string str = "default";
}

# 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};

# declaration and initialization to default key values
hash<Container> c1();

# declaration and initialization with explicit values
hash<Container> h2(("i": 10, "str": "other"));
    @endcode \n
    This type is supported at parse-time and at runtime; to convert such values to an untyped hash, assign it
    to a @ref hash_type "hash" lvalue, use @ref cast "cast<hash>(...)" on the value, or call the @ref Qore::hash() "hash()" function on the value.  Each of these options can be used to convert a type-safe hash to an untyped hash.

    A special single argument, \c "auto", allows for the lvalue to maintain the complex hash type as in the following example:
    @code{.py}
hash h0 = ("a": 2, "b": 3);
# prints "hash"
printf("%y\n", h0.fullType());
hash<auto> h1 = ("a": 2, "b": 3);
# prints "hash<string, int>"
printf("%y\n", h1.fullType());
    @endcode

    @note a \c hashdecl may not have the name \c "auto", this name has a special meaning in complex types

    @see
    - @ref hashdecl
    - @ref hash_hashdecl_type

    @par Complex Type Support for Hashes
    The \c "hash" type also supports two type arguments which allow the value type of the hash to be declared.  The key type is also included in the declaration, but is currently restricted to type @ref string_type "string"; the following example demonstrates a hash declaration with a specific value type:\n
    @code{.py}
hash<string, int> h(("key1": 2, "key2": 3));
    @endcode \n
    This type is supported at parse-time and at runtime; to convert such values to an untyped hash, assign it
    to a @ref hash_type "hash" lvalue, use @ref cast "cast<hash>(...)" on the value, or call the @ref Qore::hash() "hash()" function on the value.  Each of these options can be used to convert a type-safe hash to an untyped hash.

    @see @ref hash_complex_type

    @par Pseudo Class for Type Hash:
    @ref Qore::zzz8hashzzz9

    @par Type Code:
    @ref Qore::NT_HASH

    @par Type Name:
    \c "hash"

    @see @ref hash_type

    @note
    - Trailing commas are ignored in immediate hash declarations (as with @ref list "lists") to allow for easier insertion and deletion of elements in source code.
    - Hashes are dereferenced in one of two ways, either using curly brackets: \c "{" and \c "}", where any valid %Qore expression can be used, or using the dot "." hash member dereferencing operator, where literal strings can be used.  In the case of using a literal string with the dot operator, keep in mind that the string is always interpreted as a literal string name of the member, even if there is a constant with the same name.  To use the value of a constant to dereference a hash, use curly brackets with the constant: ex: @code{.py} hash{MyConstant} @endcode
    - Use quotes around the member name when dereferencing hash members with the same name as a %Qore keyword or other name that is not a valid identifier: ex: @code{.py} hash."my-tag-1!" @endcode
    - %Qore hashes preserve key insertion order to support consistent serialization and deserialization to strings (such as XML, JSON, or YAML strings) or data encoding formats, therefore the @ref keys "keys operator" and the <hash>::keys() pseudo-method will always return the hash keys in insertion/creation order
    - Dereferencing hash values that are not present in the hash will return @ref nothing; to catch typos in hash member names when dereferencing a hash, you can use an @ref object "object" and declare a list of allowed public members in the @ref qore_classes "class definition" (in which case dereferencing a non-existant member will cause a parse exception to be thrown, if the object's class is known at parse time, otherwise it will cause a runtime exception), also the Qore::ListHashIterator class allows for hash members to be dereferenced transparently and will throw an exception if a non-existant member name is given (to catch typos).
    - There are several pseudo-methods implemented in both the @ref Qore::zzz8hashzzz9 and the @ref Qore::zzz8nothingzzz9 pseudo-classes that provide asy access to special hash iterators:
      - <hash>::iterator() provides easy access to a hash value iterator; it returns a @ref Qore::HashIterator "HashIterator" object where the @ref Qore::HashIterator::getValue() "HashIterator::getValue()" method returns the current key value
      - <hash>::keyIterator() provides easy access to a hash key iterator; it returns a @ref Qore::HashKeyIterator "HashKeyIterator" object where the @ref Qore::HashKeyIterator::getValue() "HashKeyIterator::getValue()" method returns the current key string; this is very useful when using a hash as a simulation for a set of strings and quickly iterating the hash with the @ref map "map operator", for example
      - <hash>::pairIterator() provides easy access to a hash key/value pair iterator; it returns a @ref Qore::HashPairIterator "HashPairIterator" object where the @ref Qore::HashPairIterator::getValue() "HashPairIterator::getValue()" method returns a hash with \c "key" and \c "value" keys giving the key-value pair for the current iterator position in the hash
      - <hash>::contextIterator() provides easy access to the current element in a hash of lists such as that returned by the @ref Qore::SQL::Datasource::select() "Datasource::select()" or @ref Qore::SQL::DatasourcePool::select() "DatasourcePool::select()" methods; it returns a @ref Qore::HashListIterator "HashListIterator" object where the @ref Qore::HashListIterator::getValue() "HashListIterator::getValue()" method returns a hash of the current row value (which is very useful when iterating query results with the @ref map "map operator", for example); this class also provides a @ref Qore::HashListIterator::memberGate() "memberGate() method" that allows the iterator object itself to be dereferenced directly as a hash representing the current row (which is useful in @ref foreach "foreach statements" for example).
      - the corresponding pseudo-methods in the @ref Qore::zzz8nothingzzz9 pseudo-class return empty @ref Qore::SingleValueIterator "SingleValueIterator" objects so that these pseudo-methods can be safely used with @ref hash_or_nothing_type "*hash" (ie "hash or nothing") value types.
    - the @ref hmap "hash version of the map operator" is used when a literal single member hash expression with curly brackets is given to the @ref hmap "map" operator; this version of the map operator can be used to build a hash dynamically from a list or iterator expression

    <hr>
    @section object Object

    @par Description:
    %Qore objects are instantiations of a @ref qore_classes "class". They have members (like a hash; values associated to string keys), and methods. The @ref qore_classes "class definition" specifies the methods that run on objects of that class, public and private members, static methods and variables, etc associated with the class (however note that static methods do not run in the scope of an object).  See @ref qore_classes for information on how to create a class in %Qore.

    @par Immediate Value Example:
    @code{.py} new Mutex();@endcode

    @par Pseudo Class for Type Object:
    @ref Qore::zzz8objectzzz9

    @par Type Code:
    @ref Qore::NT_OBJECT

    @par Type Name:
    \c "object"

    @see
    - @ref object_type
    - @ref object_functions

    @note each %Qore type has a "pseudo-class" associated with it (the default is @ref Qore::zzz8valuezzz9); methods from the data type's "pseudo-class" can be run on any value of that type; see "Pseudo Class for Type" headings in @ref basic_data_types for more information.  Pseudo-methods can be overridden in classes; if a class implements a method with the same name and signature as an object pseudo-method, then the class' method will be executed instead of the pseudo-method.

    @subsection object_overview Object Overview
    The recommended way to instantiate an object is to declare its type and give constructor arguments after the variable name in parentheses as follows:
    @code{.py}
class_name_or_path var_name([argument list])
    @endcode

    For example (for a constructor taking no arguments or having only default values for the aguments, the list is empty):
    @code{.py}
# simple object declaration and instantiation
Mutex m();
# this is equivalent to the above
object<Mutex> m1();
    @endcode

    Objects can also be instantiated using the @ref new "new operator" as follows.

    @code{.py}
new class_identifier([argument list])
    @endcode

    For example:

    @code{.py}
Mutex m = new Mutex();
    @endcode

    Objects have named data members that are referenced like hash elements, although this behavior can be modified for objects using the memberGate() method. Object members are accessed by appending a dot '.' and the member name to the object reference as follows:

    @code{.py}
object_reference.member_name
    @endcode

    For more information, see @ref class_members.

    Object methods are called by appending a dot '.' and a method name to the object reference as follows:

    @code{.py}
object_reference.method_name([argument_list])
    @endcode

    Or, from within the class code itself to call another method from inside the same class hierarchy:

    @code{.py}
method_name([argument_list])
    @endcode

    For more information, see @ref object_method_calls.

    The object references above are normally variable references holding an object, but could be any expression that returns an object, such as a @ref new "new expression" or even a function call.

    @note Objects are treated differently than other %Qore data types; they are only explicitly copied (see @ref object_references for more information). Any object instantiated with the @ref new "new operator" will remain unique until deleted or explicitly copied. An explicit copy is made with the copy method, and does not always guarantee an exact copy of the source object (it depends on the definition of the copy method for the class in question).

    Objects exist until they go out of @ref object_scope "scope", are explicitly deleted, or their last thread exits. For detailed information, see @ref qore_classes.

    @see @ref object_type

    @subsection object_references Object References

    In %Qore objects are treated differently from all other data types in that they are by default passed as arguments to functions and methods by passing a copy of a reference to the object (similar to Java's handling of objects). That means that passing an object to a function that modifies the object will by default modify the original object and not a copy, however reassigning a local parameter variable assigned an object passed as an argument (that is only assigned to a local variable in the calling function) will not result in deleting the object, but rather decrement its scope reference count (note that if the object were created as a part of the call and reassigning the variable would cause the object's scope reference count to reach zero, then the object would be deleted in this case).

    Assigning an object to a variable has the same effect; a copy of a reference to the object is assigned to the variable. This results in prolonging the object's scope (by owning a new copy of a reference to the object).

    An example:
    @code{.py}
sub test2(any x) {
   # we can modify the original object like so:
   x.member = "tree";

   # here we re-assign x, but since the object is also assigned
   # to o in the calling function, the object's scope is still
   # valid, and therefore nothing happens to the object
   x = 1;
}

sub test() {
   TestObject o();

   # here we pass a copy of a reference to the object in o
   test2(o);

   # this will print out "ok\n", because the object is still
   # valid and the member has been set by test2()
   if (o.member == "tree")
      print("ok\n");
}
# when test() exits, the object in o will go out of scope
# and be deleted
    @endcode

    If, however, an object is passed by reference, then the local variable of the called function that accepts the object owns the scope reference of the calling functions's variable.

    An example:
    @code{.py}
sub test2(any x) {
   # we can modify the original object like so:
   x.member = "tree";

   # here we re-assign x, and since we own the only scope
   # reference to the object, the object will go out of
   # scope here and be deleted
   x = 1;
}

sub test() {
   TestObject o();

   # here we pass a reference to the object in o
   test2(\o);

   # the object has already been deleted in test2() and
   # therefore nothing will be printed out
   if (o.member == "tree")
      print("ok\n");
}
    @endcode

    @note that when parse option @ref allow-bare-refs "%allow-bare-refs" is set, then variable references as in the above examples are made without the \c "$" character.

    @subsection object_scope_and_gc Object Scope and Garbage Collection

    @subsubsection garbage_collection Garbage Collection and Resource Management

    All complex data structures in %Qore use atomic references to support copy-on-write semantics, but objects are unique since they are always passed with a copy of a reference to the object (similar to Java's object handling), therefore it's possible to make recursive references to objects, a fact which complicates object scope management and garbage collection.

    The <a href="http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization">RAII idiom</a> for resource management is a fundamental feature of Qore's design;
    the language should always guarantee that objects are destroyed and therefore their associated resources are managed and memory freed when objects go out of scope.
    This is tricky when objects have recursive references; other languages use a variety of approaches to deal with this (for example, a mark-and-sweep algorithm in a
    dedicated garbage collection thread) which can have various drawbacks (such as non-deterministic performance impacts, such as when the garbage-collection thread
    grabs global locks to verify references, etc).

    %Qore supports deterministic garbage collection of objects even when objects participate in recursive directed graphs.  In this case the deterministic aspect of
    %Qore's garbage collection algorithm means that objects are collected immediately when they have no more valid references to them.  In case of recursive references,
    it means that no valid references are pointing to any object in the recursive graph; in other words, the scope references for every object participating in the
    recursive graph are only due to references related to recursive references.  In this case, the affected objects are collected immediately by having their destructors
    run.

    The consequence of this is that destructors are run immediately when the objects are no longer valid, and therefore the
    <a href="http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization">RAII idiom</a> for resource management is supported in %Qore even when objects
    participate in recursive directed graphs.

    Some examples of <a href="http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization">RAII</a> in builtin %Qore classes are (a subset of possible examples):
    - the @ref Qore::Thread::AutoLock "Autolock" class releases the @ref Qore::Thread::Mutex "Mutex" in the destructor (this class is designed to be used with scope-bound exception-safe resource management; see also the @ref Qore::Thread::AutoGate "AutoGate", @ref Qore::Thread::AutoReadLock "AutoReadLock", and @ref Qore::Thread::AutoWriteLock "AutoWriteLock" classes)
    - the @ref Qore::SQL::Datasource "Datasource" class closes any open connection in the destructor, and, if a transaction is still in progress, the transaction is rolled back automatically and an exception is thrown before the connection is closed
    - the @ref Qore::SQL::DatasourcePool "DatasourcePool" class closes all open connections in the destructor, and, if a transaction is still in progress, the transaction is rolled back automatically and an exception is thrown before the connection is closed
    - the @ref Qore::ReadOnlyFile "ReadOnlyFile" and @ref Qore::File "File" classes close the file descriptor in the destructor if it's open
    - the @ref Qore::Thread::RWLock "RWLock" class throws an exception if destroyed while any thread is still holding the lock; note that in this case the underlying object is only destroyed when all threads holding locks have released their locks; this is handled with %Qore's @ref thread_resources "thread resource handling" and strong references to the underlying @ref Qore::Thread::RWLock "RWLock" object while @ref thread_resources "thread resources" are held
    - the @ref Qore::Socket "Socket" class first shuts down any TLS/SSL connection and then closes the connection in the destructor if it's open
    - the @ref Qore::Thread::ThreadPool "ThreadPool" class detaches all currently in-progress worker threads, cancels pending tasks not yet executed (by calling their cancellation closure, if any), terminates the worker thread and destroys the thread pool

    @note %Qore also supports scope-related resource management support in the form of the @ref on_exit "on_exit", @ref on_success "on_success", and @ref on_error "on_error" statements

    @subsubsection object_scope Object Scope

    As mentioned in the previous section, objects are automatically collected and have their destructors run when their scope-relevant reference count reaches zero or when
    the scope-relevant reference count is equal to the number of recursive references for every object in a recursive directed graph (note that objects can be deleted
    manually at any time by using the @ref delete "delete operator"). Whenever an object is collected, the object's destructor method as defined in the object's class is run on the object (multiple destructor methods can be run on objects instantiated from a class hierarchy).

    The following affect objects' scope:
    - external references to the object: an object's automatic scope is prolonged as long as the object has valid external references
    - object method thread launched within the object: if a member function thread was launched from within the object using the @ref background "background operator", the object's automatic scope is prolonged to the life of the new thread. Object threads started externally to the object (i.e. not directly from an expression with the @ref background "background operator" within a method) will not prolong the scope of the object.

    @note If an object with running threads is explicitly deleted, and this case is not handled in the object's destructor() method (by ensuring that all other running threads terminate gracefully), exceptions will be thrown in other threads at any attempt to access the already-deleted object.  The fact that object threads and closures can prolong object scope means, for example, that objects assigned to local variables can exist for longer than the scope of their host variable if they have one or more methods running in other threads or if closures created from within the object still exist at the time the local variable goes out of scope.  For more information about threading, please see @ref threading

    @since As of %Qore 0.8.10 the existence of a closure created within the object no longer prolongs the scope of the object; the closure encapsulate the object's state (along with any local variables referenced within the closure), but if the object goes out of scope while the closure still exists, then any references to the object after the object has been destroyed will cause \c OBJECT-ALREADY-DELETED exceptions to be thrown.  This addresses memory and reference leaks caused by recursive references when closures encapsulating an object's scope are assigned to or accessible from members of the object.

    @subsection copying_objects Copying Objects

    To explicitly generate a copy of an object, the \c copy() constructor must be called. This is a special method that exists implicitly for every class even if it is not explicitly defined (like \c constructor() and \c destructor() methods). The implicit behavior of the \c copy() constructor is to create a new object with new members that are copies of the original members (except objects are created as copies of references to the object). Then, if any \c copy() method exists, it will be executed in the new object, passing a copy of a reference to the old object as the first argument to the \c copy() method.

    @note In a class hierarchy copy() methods are called in the same order as constructor() methods.\n\n
    Not all built-in classes can be copied. Classes not supporting copying will throw an exception when the \c copy() methods are called. See the documentation for each class for more information.
*/
