ROOT Containers

ROOT provides a number of objects whose function is to hold other objects (i.e. they are containers). ROOT calls them collections and provides a number including arrays, lists and trees. As these collections have a number of features in common it is worth making some general remarks about them.

Containers: General Characteristics

Determining the Class of Contained Objects

Most containers may hold heterogeneous collections of objects and then it is left to the user to correctly cast the TObject pointer to the right class. Casting to the wrong class will give wrong results and may well crash the program!. So the user has to be very careful. Often a container only contains one class of object, but if it really contains a mixture, it is possible to ask each object about its class using the InheritsFrom message. For example if MyObject is TObject pointer:-
if ( MyObject->InheritsFrom("REROOT_FLSDigit" ) {
  printf("Object is a REROOT_FLSDigit\n");
}
As the name suggests, this test works even if the object is a subclass of REROOT_FLSDigit. The member function IsA can be used instead of InheritsFrom to make the test exact.

Iterators: Processing a Collection

The concept of processing all the members of a collection is generic, i.e. independent of any specific representation of a collection. To process each object in a collection needs some type of cursor that is initialised and then stepped over each member of the collection in turn. Collection objects could provide this service but there is a snag: as there is only one collection object per collection there would only be one cursor. Instead, to permit the use of as many cursors as required, they are made separate classes called an iterators. For each type of container organisation there is an associated iterator class that knows how to sequentially retrieve each member in turn. The relationship between a collection and its iterator is very close and may require that the iterator has full access to the collection (i.e. it is a friend).

Of course specific collections e.g. arrays may have additional ways of systematically processing every member, but the concept of iterating applies to all.

Foundation Classes

All collections are based on the fundamental classes: TCollection, TIterator and TIter. They provide an excellent example of the Concept: Interfaces & Abstract Classes. TCollection and TIterator are so generic that it is not possible to create objects from them; they are only used as base classes for other classes (i.e. they are abstract base classes).

TCollection

This class defines the minimum set of member functions that all collections must support. These include:-

TIterator

This class defines the minimum set of member functions that all iterators must support. These include:-

TIter

As stated above, the TIterator class is abstract; it is not possible to create TIterator objects. However, it should be possible to write generic code to process all members of a collection so there is a need for a generic iterator object. A TIter object act as generic iterator. It provides the same Next and Reset messages as TIterator although it has no idea how to support them! It works as follows:- So TIter simply acts as a wrapper for an object of a concrete class inheriting from TIterator.

To see this working in practice, consider the TObjArray collection. Its associated iterator is TObjArrayIter. Suppose myarray is a pointer to a TObjArray i.e.

TObjArray *myarray; which contains MyClass objects. To create a TIter object called myiter:- TIter myiter(myarray); As shown in the diagram, this results in several messages:-

Now to define a pointer for MyClass objects and set it to each member of the of the TObjArray:-

MyClass *myobject; while( myobject=(MyClass *)myiter.Next() { \\ Process myobject } The heart of this is the myiter.Next() expression which does the following:-

Sometimes the TIter object is called next, and then instead of writing

next.Next() which is legal, but looks rather odd, iteration is written as:- next() This works because the function operator () is defined for the TIter class to be equivalent to the Next message.

ForEach Macro

The situation in which the same message has to be sent to every object in a collection is so common, that the C++ macro ForEach, which is defined in the TCollection.h header file, which will generate all the code (although to be honest is not very readable code!) to do it. For example to send every MyClass object the DoIt(123) message:- myarray->ForEach(MyClass,DoIt)(123) ForEach takes two arguments, the class of the objects held in the container and the message to be sent to them. The macro must be preceded by the name of the collection with the appropriate member selection operator. So, if myarray were a collection rather than a pointer to a collection, the line would read:- myarray.ForEach(MyClass,DoIt)(123) The macro must be followed by the arguments to be supplied to the member function.
Go Back to the The ROOT Crib Top Page


If you have any comments about this page please send them to Nick West