The data struture in OpenMA is mostly a tree-like structure (more exactly it is a graph as a node can have several parents). The general idea is to store data in a dynamic structure without the need to modify the internal storage each time a new category of data is added (e.g. pressure, GPS, etc.). Thus, it would be simpler to integrate new kind of file formats as well as new models.

Children nodes are owned by their parents. Thus only the first parent(s) (e.g. the root' tree) has to be deleted. This one can be stored in a smart pointer (shared, unique pointer) to not manage its deletion. For example:

ma::Node* root = new ma::Node("root");
ma::Node* leafA = new ma::Node("leafA",&root); // Owned by the root
ma::Node* leafB = new ma::Node("leafB",&root); // Owned by the root
// ...
delete leafB; // Early deletion. LeafB is removed from the root's children.
// ...
delete root; // End of the program/function, the root is deleted and leafA at the same time.

It is strongly adviced to create nodes on the heap (using the new operator). This is important for the child management when some of them might be replaced internally (for example using the method replaceChild()), otherwise your program might crash due to an undefined behaviour.

In case you want to use Node objects only as a tree data structure without later modification (e.g. child replacement, child deletion), these ones can be created on the stack (using regular constructor), they must follow a specific order: the parent must be created before the children. For example:

ma::Node root("root");
ma::Node leafA("leafA",&root); // Owned by the root
ma::Node leafB("leafB",&root); // Owned by the root
// ...
// end of the program/function. Nodes leafB, leafA, and root are destroyed in thid order.

In case this order is not respected, a runtime error (or crash) should occur like in the next example:

ma::Node leafA("leafA");
ma::Node root("root");
ma::Node leafB("leafB",&root); // Owned by the root
leafA.addParent(&root); // Owned by the root
// ...
// End of the program/function, leafB is destroyed, then root which destroys also its children. What about leafA?

In the previous example, the remaining child of root (pointer to leafA) is a local variable and calling its destructor is incorrect. Thus, when the variable leafA goes out of scope, its destructor is called again. The same memory is freed two times that should crash the program.

Finally, to declare a custom node type (i.e. a new inheriting class), several macros can be used:

Appends a node if this one is not already a parent. In case this node is added, node is attached as parent and its state is set to modified.

Uma::Node::child (unsigned index) const noexcept

Returns the node associated with the given index or null if out of range. By default the returned type is Node but can be set to any inheriting class. If the set type does not correspond to the internal type for the given index, a null value is returned.

Returns the child with the given name and which can be casted to the type T. You can refine the search by adding properties to match. The search can be done recursively (by default) or only in direct children. The latter is available by setting recursiveSearch to false. There are three ways to use this methods.

You can explicitely define the type T and the name.

ma::Event* = root.findChild("RHS2");

You can also only give the type T. Then the first node found with this type will be returned. In this case, no name is given as it is set to a null string (not empty)

ma::ForcePlatform* = root.findChild();

By default, the default type is set to ma::Node*, so you can even search for a "node" without giving the type..

In addition, you can add properties to refine the research. Sometimes this could be usefull to distinguish events with the same name but different properties. As presented in the following examples, it is adviced to give matching properties in an initializer list using curly brackets due to the use of the move semantics in the method. Inside, each matching property is given as a pair {key, value} given also by an initializer list. So, for a single matching property, two pairs of curly brackets are used but this is correct.

Returns the children with the given name and which can be casted to the type T. You can refine the search by adding properties to match. The search can be done recursively (by default) or only in direct children. The latter is available by setting recursiveSearch to false. As with the method findChild(), you can explicitely or implicitely give the type and/or the name of the children. For example:

Returns the value's property associated to the given key. The value is a Any object and can be converted to several types implicity. For example, the following code shows two ways to extract property's value.

Retrieves the first path existing between the current node and the given node. If no path exists between both, then an empty list is returned, The first node in the retrieved path is the current one, while the last is the node to search.

voidma::Node::setDescription (const std::string & value) noexcept

Sets the description of the node. By default the description is empty. You can also modify this information using the property 'description'.

Note

The state of the node is not modified if the description is different.

voidma::Node::setName (const std::string & value) noexcept

Sets the name of the node. You can also modify this information using the property 'name'. In case the value is different, the state of the node is set to modified.

Sets the property key with the given value. The value can be an Any object or a type supported by it (e.g. int, double, std::string,). In case the property does not exist, or its value is different, or it is removed, the state of the node is set to modified.

Define the public method isCastable() used to determine if the object can be cast to the given type.

Note

This macro must be included by every inheriting Node classes to be correctly recognised as suche. For example, the function node_cast() needs the use of this macro to correctly work.

OPENMA_DECLARE_STATIC_PROPERTIES_BASE
(class, ... )

Add a private StaticProperties structure and the methods staticProperty() and setStaticProperty(). The classes that can use this macro may need to derive of the class ma::Node (or any inheriting class) but its private implementation must be a based one.

Add a private StaticProperties structure and the methods staticProperty() and setStaticProperty(). Compared to the macro OPENMA_DECLARE_STATIC_PROPERTIES_BASE(), this one is for derived private implementation.