- Home
- Services
- FAQ
- Portfolio
- Latest News
- Datasoft Talk
- Tree Container Library
- Overview
- Usage
- Interface
- Iterators
- Iterator Overview
- Child Iterators
- Reverse Iterators
- Descendant Iterators
- Ordered Iterators
- Design
- Auxiliary Code
- Implementation
- basic_tree
- associative_tree
- tree/multitree
- unique_tree
- sequential_tree
- child iterators
- descendant iterators
- ordered-iterators
- Examples
- STL Compatibility
- Overview
- STL Containers
- STL Algorithms
- Development History
- Version History
- Credits
- Documentation
- Download
- About Us
The Tree Container Library
A C++ generic tree container library which, not only works much like the STL, but is also compatible with the STL algorithms. The Tree Container Library is used worldwide in thousands of industries and organizations. It is completely free to use, and is licensed under the zlib/libpng license.
Using the Tree Container Library (TCL) is much like using the container classes in the STL. The syntax and operation is very similar. First, you will need to decide which of the four tree containers will fit your needs. The difference between the tree containers is described in the overview.
The tree and multitree both accept two template arguments, the second being optional. The first template parameter represents the data type you will
be storing in the container, hence forward called the stored_type. The stored_type
can be either a basic data type, such as an int or a string, or a user defined type (class). Objects of stored_type are considered as elements,
which are stored within the nodes
of the tree. The second template parameter represents the node comparison operation, a functor, used in the searching and ordering of the nodes, hence
forward called the node_compare_type. If not supplied, this
template parameter defaults to std::less<stored_type>
. So, if the container stores a basic type such as int's, the node_compare_type
will default to std::less<int>
. If the container stores class objects, then you could either supply a < operation for the class
which would be used when the node_compare_type
defaults to std::less<stored_type>
, or, create and use a functor for
the second parameter.
The unique_tree accepts three template arguments, the last two being optional. The first two template parameters are the same as with the tree and multitree described above. The third template parameter represents another node comparison operation, called the node_order_compare_type which is used for special iterators called ordered_iterators which can be used to determine an alternate ordering of nodes within their parent. An alternate ordering for child nodes can be handy if the comparison operation used to distinguish unique nodes within the unique_tree is not the desired ordering you wish to be used to traverse child nodes within a single parent.
The sequential_tree accepts a single template argument, which is required. This parameter represents the data type you will be storing in the
container, stored_type
, discussed above. Since this container does not naturally order the nodes within their parent, there is not a
node_compare_type that is needed for sequential_tree. The nodes remain in the order in which they were inserted into the tree. This container does,
however, offer sorting operations for any or all of the nodes, so the order of the nodes can be changed if desired.
Given the above information, the following are simple declarations of the four tree containers:
-
tree<std::string> my_tree;
-
multitree<int> my_tree
-
unique_tree<CMyClass> my_tree;
-
sequential_tree<std::string> my_tree;
#include "tree.h" #include "multitree.h" #include "unique_tree.h" #include "sequential_tree.h" #include <string> class CMyClass { public: CMyClass() : key(0) {} // needs default constructor CMyClass(int key_, const std::string& text_) : key(key_), text(text_) {} const std::string& get_text() const { return text; } friend bool operator <(const CMyClass& lhs, const CMyClass& rhs) { return lhs.key < rhs.key; } private: int key; std::string text; }; struct CAltKey { bool operator()(const CMyClass& lhs, const CMyClass& rhs) { return lhs.get_text() < rhs.get_text(); } }; int main(int argc, char* argv[]) { using namespace tcl; tree<CMyClass> my_tree; multitree<CMyClass, CAltKey> my_multi_tree(CMyClass(0, "Root")); unique_tree<CMyClass, std::less<CMyClass>, CAltKey> my_unique_tree; sequential_tree<CMyClass> my_sequential_tree; return 0; }
The usage of class objects in the trees is shown to the right. Here, objects of the class, CMyClass
, will be stored in the tree
containers. Notice that MyClass defines a < operator, which is used by the default std::less<>
parameter for the associative
trees. This operation will be used by the default (or explicitly supplied) std::less<>
parameter. If a < operation is not
defined for the class, then an explicit comparison functor must be declared and supplied for the second template parameter for the associative trees.
Notice in the sample code, that the tree declaration provides no second parameter, so the < operator will be used for node comparisons. In the
multitree declaration, CAltKey
is supplied for the second parameter, which will be used for the node comparisons. In the unique_tree
declaration, both the second and third template parameters are supplied. The second template parameter, node_compare_type
is just std::less<CMyClass>
,
which is the default. Since the third parameter is being explicitly supplied, the second parameter must also be explicitly supplied, even though it
is the default value. The third parameter supplies the comparison CAltKey
which can be used to iterate the nodes within a parent in an
alternate order. The sequential_tree needs no comparison operation. However, since a < operation is defined for CMyClass, this comparison
operation will be used for sequential_tree's sort() operation if sort() is not supplied a parameter.
Note that in the declaration of the multitree, a newly created element is passed to the constructor. This will set the element of the root node. Normally, an element is supplied to a node during an insert operation, but the
root node must have it's element
set via the constructor.
Creating an iterator for the containers is much like creating an iterator for STL containers. Iterators are used for more than just iterating over the
nodes in the trees. Indeed, the find()
, insert()
, and other operations return a child iterator. There are three types of
iterators in the TCL.
-
child iterators
child iterators are available for all three tree containers. They iterate only over the children of the specified node. Child iterators traverse the breadth, not depth of the specified node. -
descendant iterators
descendant iterators are also available for all three tree containers. Descendant iterators iterate over the breadth and depth of a specified node, and come in three flavors.- pre order iterator
- post order iterator
- level order iterator.
-
ordered iterators
ordered iterators are available only for unique_tree. They offer an alternate order in which to traverse the children of a specified parent node.
It's helpful to think of the elements as being stored in the nodes of the trees, rather than actually being the nodes. The nodes are themselves trees
of the same type, and have the same interface as trees, for finding, searching, removing, iterating, etc. The element in any node can be retrieved
with the node's get()
operation. This tree/node operation returns a pointer to the element contained within. All elements are created on
the heap internally when inserted into the tree.
With tree, multitree, and sequential_tree, nodes must be inserted using the insert(child)
operation on the parent node for the child. So,
for these trees, you must have a pointer, reference, or iterator pointing to a node, before you can insert any child nodes into that node. For
unique_tree, this is not the case. For unique_tree, you can insert any child node into any parent node from the root node, using the insert(parent,
child)
operation available only to unique_tree.
The TCL allows for polymorphic storage without the need to store pointers in the tree container. This means that you can store elements of stored_type
and elements derived from stored_type
, and those objects will keep their original identity. To do this, you must supply a clone function
to the tree container. The container uses the clone function to copy the stored_type
or objects derived from stored_type
,
rather than using the stored_type
copy constructor. More information on this subject, as well as the standard operations for using the
trees are given in Common Interface.