/** @page qore_data Qore Data

    @tableofcontents

    @section handling_data Qore Data

    Data in %Qore is handled with QoreValue, which contains a union (@ref qore_value_u) with the following
    immediate values:
    - \c bool b
    - \c int64 i
    - \c double f
    - @ref AbstractQoreNode* n

    All complex values in Qore are descended from @ref AbstractQoreNode.

    The following are the basic data types in Qore, implemented as C++ classes descended from AbstractQoreNode, and
    their type codes, accessed via AbstractQoreNode::getType():
    - QoreStringNode (\ref NT_STRING) for the string data type
    - DateTimeNode (\ref NT_DATE) for the date/time data type
    - BinaryNode (\ref NT_BINARY) for the opaque binary data type
    - QoreNothingNode (\ref NT_NOTHING) for a type signifying no value (also equivalent to \c nullptr!)
    - QoreNullNode (\ref NT_NULL) for the SQL NULL data type
    - QoreHashNode (\ref NT_HASH) for an associative array or hash data type
    - QoreListNode (\ref NT_LIST) for a list or array data type
    - QoreObject (\ref NT_OBJECT) for the data type representing the instantiation of a Qore class (QoreClass)

    Additionally, the following classes are exposed in the library:
    - ReferenceNode (\ref NT_REFERENCE)
    - AbstractFunctionReferenceNode (\ref NT_FUNCREF)

    @see node_types.h for a complete list of all type codes.

    The fastest way to directly access data of a specific type is to use @ref QoreValue::getType() and then the
    @ref QoreValue::get() template as follows:
    @code{.cpp}
    if (val.getType() == NT_DATE) {
       DateTimeNode* dt = val.get<const DateTimeNode>();
       // .. do something with dt
    }
    @endcode

    @subsection simple_value_types Simple Value Types: QoreStringNode, DateTimeNode, BinaryNode

    The QoreStringNode (\ref NT_STRING), DateTimeNode (\ref NT_DATE), and BinaryNode (\ref NT_BINARY) classes are
    reference counted and can only be dynamically allocated.

    They are all descendents of SimpleQoreNode, meaning that dereferencing their objects cannot cause a Qore-language
    exception to be thrown (hence all support the SimpleQoreNode::deref() function), therefore the SimpleRefHolder
    class can be used to manage temporary references to objects of these types.

    For example:

    @code{.cpp}
    {
       // here getReferencedQoreStringNode() returns a QoreStringNode value with an incremented reference count
       SimpleRefHolder<QoreStringNode> qstr(getReferencedQoreStringNode());
       printf("the result is: %s\n", qstr->c_str());
       // when qstr goes out of scope, the reference count is decremented by calling SimpleQoreNode::deref()
    }
    @endcode

    @subsection unique_value_types Unique Value Types: QoreNullNode, QoreNothingNode

    QoreNullNode (\ref NT_NULL) and QoreNothingNode (\ref NT_NOTHING, but also equivalent to \c nullptr, see the note
    below) are special classes in that they may not be directly instantiated with the new operator.  They are all
    represented by single non-reference-counted objects managed directly by the Qore library.  Each of these classes
    represents a type that has only one value, therefore as a memory and performance optimization, reference counting
    is disabled for their objects.

    For QoreNullNode there is the global \ref Null object or the inline function null() that returns a pointer to this
    object.  The inline function is_null() can be used to test for a Qore SQL \ref Null value.

    QoreNothingNode has the gobal \ref Nothing object and the inline function nothing() that returns a pointer to it
    as well.

    @note that in Qore a C++ null pointer (\c nullptr) is the same as \c NOTHING, therefore the inline function
    is_nothing() should always be used to test for \c NOTHING.  Therefore the following code is incorrect:

    @code{.cpp}
    // get_qore_value() returns "AbstractQoreNode*"
    AbstractQoreNode* p = get_qore_value();
    if (!p) { // incorrect! NOTHING in Qore can be represented by nullptr or a pointer to the Nothing value
    }
    @endcode

    The code should look like this instead:

    @code{.cpp}
    // get_qore_value() returns "AbstractQoreNode*"
    AbstractQoreNode* p = get_qore_value();
    if (is_nothing(p)) { // correct test for nothing
    }
    @endcode

    @subsection container_types Container Value Types: QoreHashNode, QoreListNode, QoreObject

    The QoreHashNode (\ref NT_HASH), QoreListNode (\ref NT_LIST), and QoreObject (\ref NT_OBJECT) classes define
    container types in Qore.  QoreObject objects in particular could throw an exception when dereferenced (if the
    object goes out of scope and its destructor is run, the destructor could throw an exception).  Because container
    types can hold any type, when they are deferenced it could cause a QoreObject to go out of scope, and therefore
    the possibility that a Qore-language exception could be thrown must be taken into consideration.  Therefore, to
    dereference these objects a pointer to an ExceptionSink object must be passed to AbstractQoreNode::deref().

    The ReferenceHolder class can be used to manage temporary reference counts as follows:

    @code{.cpp}
    {
       // here a QoreHashNode value is returned with an incremented reference count
       // note that xsink must be "ExceptionSink *"
       ReferenceHolder<QoreHashNode> qhash(getQoreHashNode(), xsink);
       printf("there are %ld elements in the hash\n", qhash->size());
       // when qhash goes out of scope, the reference count is decremented
    }
    @endcode

    @subsection qoreobject Object Value Type: QoreObject

    QoreObject objects have node type \ref NT_OBJECT as returned by AbstractQoreNode::getType().

    QoreObject is special in that the implementation for objects in Qore mirrors that of Java, in that objects are
    passed by reference when used as function or method arguments, unlike other types.  However, like Java, the
    reference to the object is passed, and not the value, so, while an object passed as an argument to a function can
    be modified by that function (modifications are made to the original object), in order to write a swap method, for
    example, you would need to pass the variables by reference (or the lvalue expression by reference, as the case may
    be) to the swap function.  Unlike Java, Qore does support passing arguments by reference.

    @subsection reference_arguments Handling Reference Arguments: ReferenceNode and QoreTypeSafeReferenceHelper

    Handling lvalue references is more complicated, as access to global variables and object member references must be
    made under the appropriate thread locks.  However the QoreTypeSafeReferenceHelper class makes access to lvalue
    references much easier and takes care of all the locking, access to the lvalue expression, as well as type
    enforcement.  With the QoreTypeSafeReferenceHelper class you can get the type of the lvalue expression's value,
    get a pointer to a node with a reference count of 1 for in-place modification, or assign a new value to the
    lvalue.

    Here is an example of the use of QoreTypeSafeReferenceHelper:

    @code{.cpp}
// *string chomp(reference<string> str) {}
static QoreValue f_chomp(const QoreListNode* args, q_rt_flags_t rtflags, ExceptionSink* xsink) {
    const ReferenceNode* str = HARD_QORE_VALUE_REF(args, 0);
    QoreTypeSafeReferenceHelper ref(str, xsink);
    if (*xsink) {
        return QoreValue();
    }
    assert(ref && ref.getType() == NT_STRING);
    QoreStringNode* rv = reinterpret_cast<QoreStringNode*>(ref.getUnique(xsink));
    if (*xsink) {
        return QoreValue();
    }

    rv->chomp();
    return rv->refSelf();
}
    @endcode
*/
