Click here to Skip to main content
Click here to Skip to main content

Tree Container Library

By , 22 Aug 2007
 

Introduction

The Standard Template Library (STL) supplies C++ developers with many useful generic container classes. One type of container not found in the STL is the tree container. The Tree Container Library (TCL), presented here, is a generic tree container library which, not only works much like the STL, but is also compatible with the STL algorithms. This library and all examples presented are compatible with VC6, VC7, and VC8 (Visual Studio 2005), as well as GCC. Most other C++ compilers should have no problems with the library, if they are compliant with C++ template standards.

The complete library is available in the source code download above. Also provided in the second download above is the complete documentation of the TCL, as a CHM file. This documentation includes hundreds of pages, and examples which clearly describe the usage of the TCL. A detailed explanation and example are given for every operation of each container in the TCL, and other examples (and the source code files) are included in the documentation.

The TCL consists of four container class templates, similar to those found in the STL. These containers allow storage of basic data types or user defined types to be stored within nodes in a tree structure. Each node of the tree is considered a tree (or subtree) itself, having all the properties of the tree container which it's a part of. Thus, all operations which can be performed on the tree container can likewise be performed on any node within the tree. Iterators are provided to allow the traversal of the tree nodes. Insert and find operations, as well as various other operations, are also provided.

The TCL provides four tree containers which differ according to their intention of use:

  • tree
  • The tree container is used for tree structures in which every sibling node is unique, or rather, every child node of a particular parent can be uniquely distinguished. Non-sibling nodes need not be unique.

  • multitree
  • The multitree container is used for tree structures in which siblings need not be unique, or rather, children which have the same parent node need not be distinguishable.

  • unique_tree
  • The unique_tree container is used for tree structures in which every node in the tree is unique. Because every node in the tree is guaranteed to be unique, the unique tree offers a find_deep() operation, as well as other operations in addition to those found tree and multitree.

  • sequential_tree
  • The sequential_tree container is used for tree structures in which the tree nodes are not naturally ordered. Child nodes may be sorted after insertion, if desired. The sort order may be determined by a binary predicate, function object, or by default, the < operator of the element.

This simple class diagram below shows the relationships between the base classes and the value classes (tree container classes). basic_tree is the base tree for all tree containers, and includes basic operations common to all trees. sequential_tree derives directly from basic_tree. associative_tree also serves as a base class to the associative trees. This class contains operations common only to those trees, tree, multitree, and unique_tree.

The tree and multitree are very similar in operation and interface. The difference between the two is much like the difference between the set and multiset in the STL. The unique_tree offers many more features than the tree and multitree, since each node in the tree is unique. For example, the find() operation is available for the tree, multitree, and unique_tree, which searches for a matching child node contained within a single parent node. The unique_tree offers an additional operation, find_deep() which not only searches the parent node's immediate children, but also searches it's descendants.

The unique_tree also offers extensions to the common interface for the three tree types. All four trees offer the insert(child) operation, in which a child is inserted in the parent issuing the insert operation. The unique_tree, however, offers an extension to this and other operations. For example, the unique_tree provides another insert operation, insert(parent, child), which inserts the child in the specified parent node (if found).

tree, multitree, and unique_tree are considered associative tree containers, since they all use std::set for internal child node containment. sequential_tree, however, uses a std::vector to store the child nodes. Thus, sequential_tree does not offer find() operations like the associative tree containers do. sequential_tree has it's own unique operations, however, like multiple sort() operations which can be used to sort any/all of the child nodes within their parent. The associative trees do not need sort() operations, because their nodes are ordered naturally.

To understand the structure of the tree containers in the TCL, and how the iterators work, a good understanding is needed for the concept of a 'node' and an 'element'. In the TCL, the term 'node' is used to refer to an object in a tree structure which contains the 'stored_type' element. The stored type element (elements of which you are storing in the tree) is not considered the node itself, but is contained within the node. The tree structure is, then made up of nodes, with the top node being the root node. All nodes in the tree structure (with a couple exceptions) have the same operations and properties of the tree itself. The concept of an element is to specifiy the stored_type object which is being stored within the nodes of the tree. The node and element are considered as two seperate entities. The tree structure consists of nodes. Nodes can likewise have child nodes, and those can also have their own child nodes. The elements reside in the nodes, and are the objects which the user has specified to store in the tree.

In the TCL, iterators are used to traverse the breadth and depth of the tree containers. There are a couple different categories of iterators in the TCL. The main way to categorize the iterators is by the way they iterate over the nodes in the trees. In this respect, there are four types of iterators, listed below.

  • Child Iterators
  • Used to iterate over the immediate children of a node.

  • Reverse Child Iterators
  • Used to iterate over the immediate children of a node in reverse order.

  • Descendant Iterators
  • Used to iterate over the descendants of a tree or subtree. The descendant iterators traverse the breadth and depth of a tree/subtree. There are three popular methods for traversing a tree structure, and three types of descendant iterators to provide these traversals:

    • Pre Order
    • In this traversal, the parent node is visited first, followed immediately by any children of that node, from left to right.

    • Post Order
    • In this traversal, all children are visited first from left to right, then the children's parent.

    • Level Order
    • In this traversal, each level of the tree structure is visited in order, from top to bottom.

  • Ordered Iterators
  • These iterators are available only for unique_tree. They offer an alternate ordering scheme for the child nodes within their parent.

The TCL iterators can also be categorized by what they expose. To understand this concept, a good understanding is needed to differentiate between a 'node' and an 'element'. In the TCL, a node is considered to part of the tree structure itself. A node is used to contain the data, or elements, in the tree container. Each node in a tree structure has the same property of the tree itself. In fact, any operation which can be performed on the tree structure can also be performed (with a few exceptions) on any node in the tree structure. The nodes are in fact, subtrees within the structure.

Elements, on the other hand, are the data which is stored in the tree container. Each node in the tree structure contains an element. You decide what the elements will be in your own tree structures. The elements can be a basic type, such as and int or double, or can be a user defined type.

Now, the child, reverse, and descendant iterators all come in two varieties, which depends on what the iterators expose. Both iterator types iterate in the same fashion. The only difference is what is returned by the iterators dereference operator and pointer operator.

  • Element Iterators
  • Element iterators return a pointer/reference to the underlying element, with the -> and * operators, respectively.

  • Node Iterators
  • Node iterators return a pointer/reference to the underlying node, with the -> and * operators, respectively.

The child, reverse, and descendant iterators all come in the two varieties listed above. Thus, the TCL provides child 'element' iterators, reverse 'element' iterators, descendant 'element' iterators, and also child 'node' iterators, reverse 'node' iterators, and descendant 'node' iterators.

Normally, you will use the element iterators for most of your needs. The child element iterators are those that the TCL uses as return values from many of it's operations, like find(). These iterators are used so much more than the node iterators, that they are not given an element prefix, like the node iterators and operations are given.

And, of course, all the iterators come both the const and non-const varieties.

Not only can iterators be used to traverse the tree structures in various ways, many of the operations performed on the trees return an iterator, such as the find()and insert() operations. These iterators that are returned from many of the TCL tree container operations are normally child element iterators, unless clearly named to specify otherwise. All iterators are created using the same syntax as iterators in the STL. If a tree container contains objects of the type CMyClass, a child element iterator would be created as tree<CMyClass>::iterator it;, or tree<CMyClass>::const_iterator it;. These two child iterators traverse only a parents immediate children.

Both 'child' and 'descendant' iterators can be used with the STL algorithms, as well as both 'element' and 'node' iterators. This means that the elements, which reside in the nodes of the tree containers can be copied back and forth between containers in the STL using the 'element' iterators. The nodes can be copied back and forth between containers in the STL using the 'node' iterators. Also, virtually any STL algorithm can now be used with TCL tree containers and iterators. When used with the STL algorithms, it must be clear that the TCL 'element' iterators expose the node's elements, while the 'node' iterators expose the nodes.

As mentioned above, for the 'element' iterators, the * and -> operators are overridden to return the reference/pointer to the underlying element within the node to which the iterator points. All 'element' iterators also have a node() operation, which returns a pointer to the underlying node which contains the element. By using the element iterator's node() operation, the node's operations, such as insert() and find() are available to the element iterator. Remember that nodes have the same interface as the declared tree in which the nodes reside. (Nodes are themselves trees). The only difference between a declared tree container and one of it's nodes, is the simple fact that the declared tree container doesn't have a parent node. (It's the root node).

Points of Interest

In developing this library, I learned about the difficulties encountered when developing container classes. Looking through the implementation of STL gave me many of the ideas I needed to develop this library.

History

The latest version (4.08) of the TCL has all tree containers enclosed in the namespace 'tcl'.

More information on the design and operations, can be found here.

License

This article, along with any associated source code and files, is licensed under The zlib/libpng License

About the Author

Mitchel Haas
Software Developer Datasoft Solutions
United States United States
Member
I'm a c++ programmer in the midwest, now using VC7 at work and at home. I enjoy creating generic libraries, and template based programming.
 
I also enjoy web development (xhtml, css, javascript, php).

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralBoost Shared_Ptr Usage As Stored Object Type [modified]memberCodeSeeker27 Feb '09 - 14:28 
I've been using your fine library for some time.   I recently have
been converting software over to use of BOOST shared_ptrs for all of
the memory management benefits that it brings.   Therefore, I thought
that converting my unique_tree TCL uses over to have shared_ptrs would be as simple as changing the unique_tree typedef to:
 
typedef unique_tree<boost::shared_ptr<myclass>, less<boost::shared_ptr<myclass>, MyClassIntValueCompare>; unique_tree_type;  
 
with the comparison function changed to accept shared_ptr<myclass>& instead of MyClass&.
 
Unfortunately, it doesn't compile, complaining about find_deep function signature match problem.
 
Is there something I'm missing?  
 
modified on Friday, February 27, 2009 8:36 PM
 
<div class="ForumMod">modified on Friday, February 27, 2009 8:41 PM</div>
GeneralRe: Boost Shared_Ptr Usage As Stored Object TypememberMitchel Haas27 Feb '09 - 15:24 
Thanks for your compliment on the TCL.
Could you send me or post the exact error message your getting from the compiler?
Also, what compiler and version are you using, and can you tell me the version of TCL you're using?
The latest version of the TCL is 5.3.0.
GeneralRe: Boost Shared_Ptr Usage As Stored Object TypememberCodeSeeker27 Feb '09 - 18:06 
The latest 5.3.0 TCL version is in place.   The compiler is VC++2008, Version 9.0.30729.1 SP, .Net Framework 3.5, SP1
 
The compile error occurs on the call to find_deep as:
 
if ((unique_tree_type::const_iterator) thisTree.find_deep( shared_ptr<myClass> myClassInstPtr ) ) != thisTree.end() Smile | :)
 
The Error Text:
 
: error C2664: 'tcl::associative_iterator<stored_type,tree_type,tree_pointer_type,container_type,pointer_type,reference_type> tcl::unique_tree<stored_type,node_compare_type,node_order_compare_type>::find_deep(const stored_type &)' : cannot convert parameter 1 from 'boost::shared_ptr<T>' to 'const boost::shared_ptr<T> &'
GeneralRe: Boost Shared_Ptr Usage As Stored Object TypememberMitchel Haas28 Feb '09 - 1:07 
Hi CodeSeeker,
 
I'm suspecting that this may be a const-correctness issue, from the error you're getting...
cannot convert parameter 1 from 'boost::shared_ptr<T>' to 'const boost::shared_ptr<T> &'
 
Could you try changing your comparison operation/functor to use a const reference rather than a reference, and see if that helps?
I'm not sure if you're using a function or function object, but either would work...
 

bool MyClassIntValueCompare(const shared_ptr<myclass>& lhs, const shared_ptr<myclass>& rhs) { return lhs->somefunction() < rhs->somefunction(); }
or
struct MyClassIntValueCompare
{
   bool operator() (const shared_ptr<myclass>& lhs, const shared_ptr<myclass>& rhs) const {return lhs->somefunction() < rhs->somefunction(); }
};
 
If this isn't the issue, you might try breaking down the code where you're getting the error into two lines, instead of doing an explicit cast to a const iterator. The compiler may be having problems performing that explicit cast during the comparison to thisTree.end().
 

unique_tree_type::const_iterator it = thisTree.find_deep(shared_ptr<myClass>(myClassInstPtr));
if (it != thisTree.end()) {
  ...
}

QuestionChild iterator for root?memberRichard M. Scott4 Feb '09 - 7:37 
Is there any way to obtain a child iterator for the root of a tree? The reason I ask is that I am wrapping child iterators inside another class, as pointers to nodes in the tree (and allow next_sibling/previous_sibling operations as well as parent/first_child operations).
Thanks.
AnswerRe: Child iterator for root?memberMitchel Haas4 Feb '09 - 13:32 
Hello Scott,
 
Since child iterators need a parent, there's no way to do this. However, there are a couple of alternatives.
 
1)
You could have a 'virtual root' node. This would be a single child node within the root, which could be used as the 'working' root of the tree. Of course, calling the node's is_root() function would return false for this node, so you could make up a function isRoot() for the elements that you're storing in the tree, and set this member in the virtual root to true, so calling this element function would return true for the virtual root.
 
2)
You could use pre-order or level-order iterators instead of child iterators.
By nature, descendant iterators contain the calling node in their traversal. Pre and level order iterators traverse the called node as the first node of the iteration, and the post order iterator traverses the called node on the last node of the iteration.
 
I hope this helps.
GeneralRe: Child iterator for root?memberRichard M. Scott5 Feb '09 - 9:03 
Hi Mitchel,
 
Thanks very much for your response. I already had your first alternative in mind as a backup plan, and I think it is the approach I will take. Your second alternative is interesting. Using the level-order iterator would do the job, but would require a little more checking. (For example, a next_sibling operation would just require incrementing the level-order iterator, but requires a check to see if there actually is a next sibling).
 
Again, thanks for the response and thanks for the cool library.
GeneralNice library - couple of questionsmemberrajas1 Feb '09 - 17:19 
Thanks for the library. I have a couple of questions.
 
1. How do I go from one type of iterator to another - I need to go 'down the tree' till I get a particular node/element and then find all the siblings for that node/element. Is there a way to construct a child-iterator from the pre-order iterator (and vice-versa)?
 
2. What is your approach to possibly extending the library? In my application, I need to have the concept of a tree node 'collapsed' (its child nodes not visible) or 'expanded'. I can have this as the property of the item at that node and not make any change to the tree; or see this as a property/state of the node/tree. In the latter case, a conditional deep iteration can be defined as skipping children if a node if the node is collapsed.
 
Looking forward to answers/ideas.
GeneralRe: Nice library - couple of questionsmemberMitchel Haas1 Feb '09 - 23:52 
Hello Rajas,
 
I'm glad you like the library.
1)
There is a way to switch back between regular and descendant iterators.
To switch from a descendant to regular iterator, use the descendant iterator's base() operation. A description of the operation and example of using it is in the latest documentation. You can get the latest docs (and version of the library) at http://www.datasoftsolutions.net/tree_container_library/documentation.php[^].
To switch from a iterator to descendant iterator, just use the node() operation of the iterator, and create a descendant iterator from the node.
it.node()->pre_order_begin(); // here, 'it' is a regular iterator.
By nature, calling pre-order or level-order descendant begin() on any node will return a descendant iterator to the called node, since the called node is always part of a descendant traversal.
Again, there are examples for this in the documentation. Please see the Jan 1, 2008 'Development History' notes on this behavior in the documentation for more info on how and why the called node is contained in the descendant iterator's traversal path.
 
2)
It would be very hard to customize the library to do selective iteration. A better idea would be continue saving the 'expanded/collapsed' state in the element, as you're doing now, then to create your own forward() and reverse() operations, which accept a descendant iterator, and return a descendant iterator. These operations could examine the 'collapsed' state of the underlying element, and 'skip over' that element switching to a regular iterator, then after skipping the appropriate nodes, return a descendant iterator from the resulting iterator.
 
Let me know if you have any more questions with this idea,
 
Mitch
GeneralRe: Nice library - couple of questionsmemberrajas3 Feb '09 - 6:23 
Thanks. I got the documentation and have looked through it now. A clarification on item 2 above.
 
when I implement this forward(descendant_iterator it) function, I would check the collapsed state. and if collapsed, I would use the base() function to get the regular (child) iterator & then increment it. I would then use the node()->pre_order_begin() to return the descendant_iterator. However, is this logic valid if incrementing the regular (child) iterator gets to the end - that is the collapsed element is the last child of the current node. my forward() function should actually be returning the next sibling of the parent of this node in the next sequence.
 
If my question is not clear, I can give an example to illustrate.
GeneralRe: Nice library - couple of questionsmemberMitchel Haas4 Feb '09 - 0:40 
It sounds like the best solution for you may be to use regular (child) iterators in a recursive function.
You may try something like below.
To start the process, pass the root node to the function.
Here, it is assumed that the element's you're storing in the tree have the functions isExpanded(), and name().
 
void populateNode(tcl::tree<MyClass>* node, HTREEITEM hParent)
{
  // add node/element to your gui tree control here...
  HTREEITEM hCurrent = treeCtrl->insertItem(node->get()->name(), hParent);
  // ... blah blah
  // ... more blah
  // 
  // now, check if the node is expanded, and if so, add the node's children
  if (node->get()->isExpanded()) {
    tcl::tree<MyClass>::iterator it  = node->begin(), it_end = node->end();
    for (; it != it_end; ++it) {
      populateNode(it.node(), hCurrent);
    }
  }
}

GeneralGreat workmemberGabor Bernat8 Aug '08 - 21:41 
Just wanted to signal that you can extend the line:
 
This generic example, as well as the library, is compatible with Visual C++ 6.0, 7.0, and 8.0, as well as GCC. For the VC6 compiler, users need to un-check the Enable minimal rebuild option for successful compilation. Also for VC 6.0 users, the normal #pragma warning (disable : xxxx) directive is necessary to avoid the compiler warnings.
with Visual C++ 9.0 ( or better known as Visual Studio 2008). Tried out and they work without problems.
GeneralRe: Great workmemberMitchel Haas12 Aug '08 - 14:09 
Thanks Gabor,
 
Let's hope 2008 SP1 is friendly with the tcl. Smile | :)
QuestionHow to Add UserControl as TreeNodememberelahe babaee1 Apr '08 - 21:30 
I am having one user control (containing textBox and two buttons) , I want to add
this control as a node in a treeview control
 
Please help me
AnswerRe: How to Add UserControl as TreeNodememberMitchel Haas1 Apr '08 - 23:29 
Hello Elahe,
 
What you would like to do is certainly possible, but is no small task.
To do this, you must first subclass CTreeCtrl, and add a tree container (of your choice) as a data member...

class MyTree : public CTreeCtrl
{
...
private:
tcl::tree<mystoredtype> internal_tree;
...
}

Now, you'll need to add insert, erase, ect operations which will perform work on the internal tree member.
You might also want to add an operation like set_data(tree), where you pass a populated tree container, and the tree control will automatically display the data in the passed tree.
Most inportantly, you'll need to override CTreeCtrl's Expand() operation, so that when expanding, it will look at the internal tree container, and populate the treectrl nodes based on the contents of the internal tree container.
 
You must be well familiar with CTreeCtrl to attempt a project like this. If you're not, I'd start out by working with sample projects using a CTreeCtrl, and trace through it's Expand() operations, and most of it's other operations, since you'll be changing the behavior of these operations.
 
You'll also need to include operations to the Tree ctrl to set what text shows up in the nodes, based on the objects stored in the tree container.
 
Again, this is no easy task, but very possible with some time and work.
 
Good luck!
 
Mitch
GeneralNote for VS.Net 2002 [ver. 5.0.6]memberHarnash3 Mar '08 - 6:43 
To compile under VS.NET 2002 change:
#if defined(_MSC_VER) && _MSC_VER < 1300
 
to:
 
#if defined(_MSC_VER) && _MSC_VER <= 1300
 
Plus there are some errors under VS 2005.
GeneralRe: Note for VS.Net 2002 [ver. 5.0.6]memberMitchel Haas9 Mar '08 - 13:59 
Hello Harnash,
 
Thanks for pointing out the issue with VS2002.
 
Due to the many differences in standard compliancy between Visual Studio's C++ compilers from version 6.0 up to their latest version, I had to include many compiler guards to accomodate VC 6.0. I went to the extra trouble to make the TCL compatible with VC 6.0, because this version was very popular and in the workplace for a very long time, and still is.
 
Although I do not currently have VS2002 installed, I would agree that changing the < to <= in the guard would solve many of the problems in 2002. I feel, though, that it would create other problems for some of the guards. This is because VS2002 did fix many of the standard compliant issues in the compiler from VC6, but many were still left until VS2003. So, because the compiler guards fix many kinds of standard compliant issues, in VC6, some of those issues would be fixed in VS2002, while others would not. So, some of the guards would need to include VS2002, while others would need to exclude VS2002. I'll need to obtain and install a copy of VS2002 to determine which guards to include and which ones to exclude for VS2002. But to do this, I'll be waiting until I have the time to uninstall the later editions of Visual Studio, install VS2002, then reinstall the later versions, since I don't want to install an earlier version with the later versions installed. It may be a few months before I have the time to do this, and I can email you when I have this completed.
 
As far as any issue with VS2005, I do regularly compile the TCL with this version, and I don't encounter any issues. Could you be more specific as to what kind of errors you're seeing in VS2005?
 
Thanks,
 
Mitch
GeneralRe: Note for VS.Net 2002 [ver. 5.0.6]memberHarnash9 Mar '08 - 21:33 
Hi!
 
Thanks for the reply. I am currently using TCL in VS 2002 and 2005 and after changes I have mentioned it works fine Smile | :)
 
I have put modified files at: http://www.hardev.pl/download/TCL-5_0_7-modified.rar[^]
 
Regards,
Lucas
GeneralRe: Note for VS.Net 2002 [ver. 5.0.6]memberMitchel Haas13 Mar '08 - 17:27 
Hi Harnash,
 
Sorry it took a while to get back to you... I have a lot going on now.
I took a look at the changes you made, and it looks like I'll need to make most of these changes, and a few more. Some more changes similar to the ones you made need changed as well, but you probably aren't using the features that require those changes to be made. I won't be making all the changes you pointed out, because they would break the TCL for the GCC compiler, which is very standards compliant. So, I'll need to go over all of the include guards and determine which ones need to be changed and which ones don't so that the TCL compiles in VC2002 as well as GCC and the other MS compilers.
 
This will take a little while, since I'll wait to install VS2002 on my new machine later this spring.
Until then, it looks like you've made the necessary changes to get the TCL compiling in VS2002 for your needs.
I'll be happy to send you an email when the changes have been made, and add you to the Credits page on the TCL's web site.
 
Thanks again, and look for the new VS2002 compatible version later this spring, which will include the needed changes you pointed out,
 
Mitch
GeneralRe: Note for VS.Net 2002 [ver. 5.0.6]memberHarnash15 Mar '08 - 3:03 
Hello Mitch!
 
No problem. I'm using the TCL without problems now and since I don't compile under GCC now and I don't have time to clean up the code so I will wait for next release from you Smile | :)
 
I know that VS2002 and C++ standards are somthing comletely different.. I would love to dump it and move completely to VS2003/2005/2008 but I can't Frown | :-(
 
Keep up the good work! Smile | :)
 
Lucas
AnswerFeaturememberAndras Kiss6 Feb '08 - 4:03 
Maybe I'm missing something but didn't find any implementation of a "move" operation. Basically I would like to move a "subtree" from one node to another. Taking the Alpha sample from the documentation let's say that I want to move the subtree under "C" from the original node below "L". Is there any possibility to do this?
 
Thanks' in advance,
Andras
GeneralRe: FeaturememberMitchel Haas6 Feb '08 - 14:49 
Hello Andras,
 
The TCL is closely modeled after the containers in the STL. Since each node is a subtree/container itself, moving a subtree from one node to another would be like moving elements from one container to another in the STL, which is not allowed.
 
A solution would be to make a temporary copy of the node you wish to move, erase it, then insert it into the desired node.
I'm aware that this solution would be somewhat more expensive than 'moving' the elements, but this solution would be in accordance with STL rules.
 
In the sample example you mentioned, the snippet below, placed at the bottom of load_tree(), will 'move' node C to node L.
 

// copy node C to temp, then erase it
it = alpha_tree.end();
--it;
T temp(*it.node());
alpha_tree.erase(it);
 
// locate node L
it = alpha_tree.begin(); // at node A
it = it.node()->begin(); // at node D
++it; // at node E
it = it.node()->begin(); // at node L
 
// insert copy of node C in node L
it.node()->insert(temp);
GeneralCompiler error C2244 with Visual Studio 2005 SP1+memberGabor Benat1 Feb '08 - 19:13 
Hy there !
 
I'm using your tree container in a project and first I was using it under a Visual Studio 2005 simple version. However when I moved my code to a Visual Studio 2005 with Service Pack 1 installed i got some really crazy errors that said there is different declaration and definition off the templates.
 
After a few hour digging on the net and code I found the problem. As I thought your code is working without mistakes, but there is a Microsoft bug. Namely take a look at [url=http://support.microsoft.com/default.aspx/kb/930198]this[/url] Hotfix. After you install the HotFix there isn't any further issues. I post this here so you could add this to the download section of the Documentation (Hep Files). This way when a new user meats this particular error it won't scare him away from this nice library or it won't wast a few hours with searching the problem/solution.
 
Just want to improve ! Have a nice day ! Big Grin | :-D
GeneralRe: Compiler error C2244 with Visual Studio 2005 SP1+memberMitchel Haas2 Feb '08 - 1:51 
Hi Gabor,
 
I appreciate you taking the time to not only look into the issue, but to find the root of the issue and a fix for it.
Since I currently use VC2003 for all my development work, and only use the express editions of VS2005 and VS2008, I would not have found this issue myself. I also use GCC in an Eclipse environment to insure compatibility with that compiler. And since GCC is highly standards compliant, insures that the TCL meets those standards.
 
I updated the documentation on the site and also the download-able documentation with this info as you advised. Changes have been made to the download page and credits page on the site, and also to the Compiling Options and Credits page in the download-able documentation. I'll be updating this article and the documentation in this article very soon.
 
Thanks again for hunting this down, and hope the library is working good for you if you're using it in any of your projects!
 
Mitch
GeneralRe: Compiler error C2244 with Visual Studio 2005 SP1+ [modified]memberGabor Benat2 Feb '08 - 12:22 
You are welcome, I wasted a few hours myself of figuring out the source of the problem. It seemed strange to me that first I compiled on a Visual 2005 Standard Edition without the SP1. So I started looking over the code and it seemed OK, and as on Visual 2008 beta compiled also, I thought that it will be some version conflict,this way was the problem found.
 
As seen that the solution worked I wanted to share with you to so the other people using this code and stuck with the same error code so they won't need to waste some additional time in pursuit of the solution.
 
Glad I could help, and what more can I say that it's working perfectly in my project. Keep up the good work.
 
As side note, I just remarked that I made a typo upon registration and by this I wrote at my name Benat instead of Bernat. Edited. Sorry.
 
modified on Saturday, February 02, 2008 6:30:03 PM

GeneralHaving real-time speed issuesmemberstrik17 Jan '08 - 15:08 
Hi,
 
I'm having some severe TCL speed problems - I'm using the unique_tree structure and performing several pre_order_ iterations through the tree per frame - running combined with OpenGL.
 
I've noticed that one traverse through the entire tree takes my frame rate down from ~2000 fps to 250, and roughly halving it thereafter each time.
 
I've searched for help on the matter but haven't found anyone experiencing the same issue.
 
My current tree is only -8 members big, with most of them being attached to the root! Here's a snippet of code regarding how I'm using it:
 

tcl::unique_tree::const_pre_order_iterator io = _objects.pre_order_begin();
k8Obj *o;
while (io != _objects.pre_order_end() ) {
o = *io;
if( o->_objFlags.render && o->_objFlags.testVisibility ) {
o->TestVisibility(_activeCamera->viewFrustum());
}
io++;
}

 
In addition - I would like to ask the following question:
 
Is it possible to perform traversals of the tree - but adjust the iterator somehow to move to a different, further position in the tree?
 
For example, if a node has a particular flag "no render" set, would I adjust the traversal iterator to point to the "next sibling"?
 
Sincerely hope you're still reading this
Thanks in advance!
GeneralRe: Having real-time speed issuesmemberMitchel Haas17 Jan '08 - 16:53 
Hello Strik,
 
It's hard to know exactly what the issue could be. If you're storing pointers in the tree, it looks like your usage of the tcl iterators is correct as shown above. With the difference in frame rates, I'm not sure what you're comparing which gives you a frame rate of 250 fps. If you're comparing against using an array or a vector, there will be a decrease in performance when using a descendant iterator, since an increment operation on an array or vector is very negligible, where there is some computation involved when incrementing a descendant iterator. Whether the descendant iterator is roughly 8 times as slow, I'm not sure, but I would guess the descendant iterator would be at least 3 or 4 times slower.
 
If you're using the latest version of the TCL (Version 5.0.0 or later), the root node is also being traversed, so if that isn't intended, you should increment the descendant pre-order iterator right after the call to begin() to skip the root node, which may be adding needless time to the traversal’s operation.
 
Also, I took a quick look at the efficiency of the iterator operations, and noticed that the child iterator constructors were accepting a value iterator argument rather than a const reference, so I changed the constructors to accept the argument by const reference. This may speed up the descendant operations a little, since the descendant iterators make heavy use of child iterators in their increment and decrement operations.
The changes are available now (in version 5.0.7) at the projects website, and will be available here at CP soon.
 
Also, if there is a way you can combine using descendant iterators and child iterators, or just use child iterators, you'll see an increase in performance, as child iterators do not have all the overhead as descendant iterators for incrementing and decrementing.
 
As for custom traversals, there isn't currently a way to automatically skip past certain nodes which have a certain condition, but this can usually be done in your algorithm by checking the 'no render' state of the underlying element, and skip to the next node if it's set. I'm guessing that this is what you're currently doing.
 
Thanks, and hope this helps,
 
Mitch
GeneralRe: Having real-time speed issuesmemberstrik18 Jan '08 - 10:06 
Hello,
 
Thanks for the fast response! I was a few versions behind (I was using 4.08) so I upgraded but still no significant speed gain unfortunately.
 
At this point I'd like to add I'm not the best programmer in the world Wink | ;) So I went about my usual haphazard debugging method, trial and error. I started by the most obvious test; I unravelled the loop, knowing the number of nodes I had in my tree, I expanded it:
 

tcl::unique_tree<k8Obj*>::const_pre_order_iterator io = _objects.pre_order_begin();
io++;
if(io != _objects.pre_order_end()) {
io++;
}
if(io != _objects.pre_order_end()) {
io++;
}
if(io != _objects.pre_order_end()) {
io++;
}
if(io != _objects.pre_order_end()) {
io++;
}
if(io != _objects.pre_order_end()) {
io++;
}
//and so forth...

 
After about 10 calls to _objects.pre_order.end() I noticed a drop in frame rate to 1000fps, and subsequently around 10% further drops each thereafter.
 
To test the theory that this function call is the cause of my problem I made a const local temporary variable, And rolled out my loop with the comparision of that variable instead:
 

const tcl::unique_tree<k8Obj*>::const_pre_order_iterator end = _objects.pre_order_end();
if(io != _objects.pre_order_end()) {
io++;
}

 
However, there was no effect in frame rate. So perhaps it was in the != operator- I rolled out my loop again:
 

io++;
if(io == end) {
return;
}
io++;
if(io == end) {
return;
}

 
And this is where I am now - I hope my train of thought might make the 'problem' (if there is one at all!?) become apparent.
 
One thing is for sure - I'm very surprised at such large frame rate drops for a simple comparison like these with a Intel Duo 3.0ghz and 4GB RAM!
 
*edit* - Just a thought perhaps I've "installed" it wrong? The only thing I'm doing is #include "unique_tree.h" in my source that uses it - is there anything else that needs to be done?
 
*edit2* - The issue is happening in both debug and release modes, 32-bit.
GeneralRe: Having real-time speed issuesmemberMitchel Haas19 Jan '08 - 1:35 
Hi strik,
 
I didn't notice in your original post, that you were using the post-increment operator (io++) rather than the pre-increment operator (++io). In general, you'll always have better performance with the pre-increment operator with any object or even basic type, such as an int, since a temporary variable must be created for the post-increment operation. And, the costlier it is to create a temporary variable, the larger the difference you'll see between using a pre-increment and post-increment operation. Since there is some overhead in creating a descendant iterator copy (it has an internal child iterator, iterator stack, and a couple pointers to copy), I think you'll see a decent sized increase in performance just by using a pre-increment operator rather than a post-increment operator.
 
I also think that using a temporary iterator for pre_order_end() rather than calling pre_order_end() will increase performance slightly.
 
Looking at the implementation for !=, I don't think this could be causing the problem, since there's it involves only a couple fast comparisons.
 
I'd be interested to hear if just using pre-increment on the iterator speeds up your performance.
 
Thanks,
 
Mitch
GeneralRe: Having real-time speed issuesmemberstrik19 Jan '08 - 7:29 
woah...
 
I have to say that I doubted you - but using a pre-increment on the iterator made an absolutely phenomenal difference. From a single iteration of the tree it altered the average frame rate of my application (which iterates through the tree several times per frame) go from ~200 to ~1000.
Thanks for the advice!
GeneralNot a bug: copy constructor of sequential_tree and typedef tree_type of all kind of treesmemberC++ Hacker5 Dec '07 - 1:28 
Hello,
 
Only for the sake of completeness:
In some classes you used the typedef tree_type in your code, but
not in the copy constructor of sequential tree:
 
sequential_tree(const sequential_tree& rhs); // copy constructor
 
Shouldn't it be:
sequential_tree(const tree_type& rhs); // copy constructor
 
Why didn't you use the typedef tree_type in all classes and places?
You write in class tcl::unique_tree for example:
typedef unique_tree tree_type;
but use a few lines later unique_tree instead of the typedef.
 

 
Andreas.

GeneralRe: Not a bug: copy constructor of sequential_tree and typedef tree_type of all kind of treesmemberMitchel Haas31 Dec '07 - 2:12 
Hi Andreas,
 
I believe I originally introduced that typedef for VC6 compatibility, since there were some occasions where VC6 complained without the new typedef. I had probably replaced the original declarations in those places it was needed, and perhaps a few others, which is most likely the reason for the inconsistency. I'll be releasing a new version soon, and will take a look at these inconsistencies before the release.
 
Thanks for spotting this,
 
Mitch
GeneralSpelling mistake in the chm documentationmemberC++ Hacker4 Dec '07 - 1:30 
Hello,
 
In the tcl.chm documentation is a spelling mistake in the article
"Descendant Node Iterators" an the bottom of the page:
 
"pre_order_node_terator ..."
"post_order_node_terator ..."
"level_order_node_terator ..."
 
must be
"pre_order_node_iterator ..."
"post_order_node_iterator ..."
"level_order_node_iterator ..."
 
Kind regards
 
Andreas.

GeneralRe: Spelling mistake in the chm documentationmemberMitchel Haas31 Dec '07 - 2:07 
Hello Andreas,
 
Sorry for the late reply. Thanks for spotting this.
I'm updating the documentation to correct this typo, and hope to have it available this week.
 
Also, I'm making a correction to the library, to correctly implement the needed change to numeric_limits max.
You had given me the correct example, but I had implemented it incorrectly.
 
Thanks again,
 
Mitch
GeneralUnique Tree Example Not Compatible w/ v4.09membera carl30 Nov '07 - 12:53 
Thank you for sharing your efforts. FYI: The unique tree example from the website is not compatible w/ TCL version 4.09, but does work w/ version 3.50.
GeneralRe: Unique Tree Example Not Compatible w/ v4.09memberMitchel Haas2 Dec '07 - 1:19 
Hi Carl,
 
Thanks for the complements on the TCL.
One of the recent changes the tcl, was to wrap the library in the namespace, 'tcl'.
After doing that, all the examples and code snippets needed to be updated to reflect that change, but it looks like I missed that one. I just updated the example on the site, so it should compile and run fine now.
 
Thanks for taking the time to look into it and let me know about the problem, and hope the tcl fills the need of any projects you may need it in!
 
Mitch
Generalsequential_tree bug or bug in the examplememberC++ Hacker28 Nov '07 - 2:48 
Hello,
 
I tested the sequential_tree and tried the code from the TCL.chm help file "Usage: sequential_tree":
 
tcl::sequential_tree<std::string> my_tree;
my_tree.insert("E node");
my_tree.insert("A node");
tcl::sequential_tree<std::string>::iterator it = my_tree.insert("C node");
my_tree.insert("B node");
my_tree.insert(it, "F node");
it.node()->insert("D node");
 
This causes an Debug Assertion in std::vector
if _HAS_ITERATOR_DEBUGGING is enabled (is default on on VS 2005).
 
In the watch window, I could see, that the iterator gets corrupted after inserting "B Node".
 
Because you use std::vector internally, the sequential_tree acts like a std::vector and there the iterators may get lost if you insert new elements. So the example may be wrong, I think.

 

 

 
<div class="ForumSig">Andreas.
</div>
GeneralRe: sequential_tree bug or bug in the examplememberMitchel Haas28 Nov '07 - 13:19 
Thanks for pointing this out, Andreas.
The example is not correct, as you had thought.
As you pointed out, the iterator, 'it', is invalidated after inserting another element into the root node. I corrected the snippet on the site.
 
Thanks again! Wink | ;)
 
Mitch
Generalbasic_tree.h(73) : warning C4003: not enough actual parameters for macro 'max'memberC++ Hacker28 Nov '07 - 0:04 
Hello,
 
First: Thanks for this very good library.
 
Second: Compiling the library with VS 2005 (+SPs) and MFC with precompiled headers I get always the warning:
 
basic_tree.h(73) : warning C4003: not enough actual parameters for macro 'max'
 
I found a workaround at
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1051762&SiteID=1
that solves the min/max windows macro - stl limits problem:
 
replace line 73:
return std::numeric_limits()::max();
 
with:
return (std::numeric_limits()::max)();
 
Now the library compiles without warnings.

 

 
Andreas.

GeneralRe: basic_tree.h(73) : warning C4003: not enough actual parameters for macro 'max'memberMitchel Haas28 Nov '07 - 1:26 
Hello Andreas,
 
Thanks for the compliments on the library, and thank you very much for showing me this technique to resolve conflicts between std library operations and macros of the same name! I have encountered this issue periodically in the past, but was not aware of this very handy method to resolve the problem. Since it's a standards compliant fix, it also works well in GCC. I'll certainly be using this technique in the future to resolve these kinds of issues.
 
I've updated the library with your fix on the library's website, and also updated the version history and credits, and will be updating the library on CP soon.
 
Thanks again!
 
Mitch
GeneralCompiling under .NET (2002)memberHarnash1 Oct '07 - 6:32 
Hi.
 
I have problems with _sequential_tree. While compiling (even test suite fails) I have an error:
d:\Program Files\Microsoft Visual Studio .NET\Vc7\include\xutility(98) : error C2027: use of undefined type 'tcl::const_sequential_iterator'
with
[
stored_type=Alpha,
tree_type=tcl::sequential_tree::tree_type,
container_type=tcl::sequential_tree::container_type
]
d:\Lukasz H\_Work_\_Projects_\TreeTest\tree\child_iterator.h(135) : see reference to class template instantiation 'std::iterator_traits<_Iter>' being compiled
with
[
_Iter=tcl::const_sequential_iterator::tree_type,tcl::sequential_tree::container_type>
]
d:\Lukasz H\_Work_\_Projects_\TreeTest\tree\sequential_tree.h(160) : see reference to class template instantiation 'tcl::const_sequential_iterator' being compiled
with
[
stored_type=Alpha,
tree_type=tcl::sequential_tree::tree_type,
container_type=tcl::sequential_tree::container_type
]
d:\Lukasz H\_Work_\_Projects_\TreeTest\TreeTester\child_sequential_iterator_tester.inl(41) : see reference to class template instantiation 'tcl::sequential_tree' being compiled
with
[
stored_type=Alpha
]
d:\Program Files\Microsoft Visual Studio .NET\Vc7\include\xutility(98) : error C2146: syntax error : missing ';' before identifier 'iterator_category'
d:\Program Files\Microsoft Visual Studio .NET\Vc7\include\xutility(99) : error C2027: use of undefined type 'tcl::const_sequential_iterator'
with
[
stored_type=Alpha,
tree_type=tcl::sequential_tree::tree_type,
container_type=tcl::sequential_tree::container_type
]
d:\Program Files\Microsoft Visual Studio .NET\Vc7\include\xutility(99) : error C2146: syntax error : missing ';' before identifier 'value_type'
 
I would like to have ability to sort a tree (thus I'm using sequentional_tree) and I have encountered this problem. Thanks in advance for the help.
 
Lucas
GeneralRe: Compiling under .NET (2002)memberMitchel Haas1 Oct '07 - 16:44 
Thanks for pointing out this issue.
It looks like the test suite download didn't get updated for the latest namespace changes.
You could try adding...
using namespace tcl;
in basic_tree_tester.inl, to resolve the issue.
The test suite download has also just been updated, to add this change.
 
Thanks again for pointing out the issue, and please let me know if this doesn't resolve the problem,
 
Mitch
QuestionRe: Compiling under .NET (2002)memberHarnash1 Oct '07 - 21:43 
Thanks for quick answer! Smile | :)
 
I still have problems building Your Test Suite (latest one) Frown | :( I have put my project in http://www.harnash.eu/download/TreeTester.zip You can look at this and prerhaps it will help You solve the issue (detailed info on errors can be found in BuildLog.htm in the archive).
 
Lucas
AnswerRe: Compiling under .NET (2002)memberMitchel Haas2 Oct '07 - 1:54 
Hi Lucas,
 
Thanks for supplying your project files to test.
The problem seems to be due to the use of...
 
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
 
which is redefining the heap memory allocator.
Commenting out these lines in TreeTester.cpp resolves the problem.
I'm not sure what the reason for this define is, and it looks like a MFC construct,
but you might be able to find some info on it on the web.
 
Hope this will work for you,
 
Mitch
QuestionRe: Compiling under .NET (2002)memberHarnash2 Oct '07 - 2:32 
Mitchel Haas wrote:
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

 
This was put there by Visual Studio wizard... anyway removing this doesn't seem to help at all Frown | :(
I have tried other STL implementations - STLPort and it seems to compile but for other reasons I can't use STLPort in my project... So it seems that this issue is more M$ specific...
 
Thanks for quick response. I will try to find some workaround but I don't have time for that now. Just drop a line or two if You manange to find solution Smile | :)
 
Were You even able to reproduce the problem?
 

 
Lucas
AnswerRe: Compiling under .NET (2002)memberMitchel Haas2 Oct '07 - 17:08 
I was able to reproduce a problem, where I was getting the following compile error:
d:\Microsoft Visual Studio .NET 2003\Vc7\include\xtree(1133): error C2061: syntax error : identifier '_Wherenode'
After removing the lines mentioned above, it compiled fine.
I'm not sure if this is the message that you're seeing now, though.
 
I am, however, using Studio 2003. It's possible this issue exists only in Studio 2002.
You could try disabling "Enable Minimal Rebuild", as that can cause issues in VC6 with templates.

GeneralRe: Compiling under .NET (2002)memberHarnash3 Oct '07 - 0:55 
The problem I have is:

template <class _Iter>
struct iterator_traits
{ // get traits from iterator _Iter
typedef typename _Iter::iterator_category iterator_category;
typedef typename _Iter::value_type value_type;
typedef typename _Iter::difference_type difference_type;
typedef difference_type distance_type; // retained
typedef typename _Iter::pointer pointer;
typedef typename _Iter::reference reference;
};

and compiler reports error:
d:\Program Files\Microsoft Visual Studio .NET\Vc7\include\xutility(98) : error C2027: use of undefined type 'tcl::const_sequential_iterator<stored_type,tree_type,container_type>'
        with
        [
            stored_type=Alpha,
            tree_type=tcl::sequential_tree<Alpha>::tree_type,
            container_type=tcl::sequential_tree<Alpha>::container_type
        ]
        d:\Lukasz H\_Work_\_Projects_\TreeTest\tree\child_iterator.h(135) : see reference to class template instantiation 'std::iterator_traits<_Iter>' being compiled
        with
        [
            _Iter=tcl::const_sequential_iterator<Alpha,tcl::sequential_tree<Alpha>::tree_type,tcl::sequential_tree<Alpha>::container_type>
        ]
        d:\Lukasz H\_Work_\_Projects_\TreeTest\tree\sequential_tree.h(160) : see reference to class template instantiation 'tcl::const_sequential_iterator<stored_type,tree_type,container_type>' being compiled
        with
        [
            stored_type=Alpha,
            tree_type=tcl::sequential_tree<Alpha>::tree_type,
            container_type=tcl::sequential_tree<Alpha>::container_type
        ]
        d:\Lukasz H\_Work_\_Projects_\TreeTest\TreeTester\child_sequential_iterator_tester.inl(41) : see reference to class template instantiation 'tcl::sequential_tree<stored_type>' being compiled
        with
        [
            stored_type=Alpha
        ]
d:\Program Files\Microsoft Visual Studio .NET\Vc7\include\xutility(98) : error C2146: syntax error : missing ';' before identifier 'iterator_category'
 
and similiar errors for all other members of this structure.
 
I'm pretty sure it is some small bug which can be easily fixed but... Wink | ;)
GeneralA need of a demo projectmembermin_2_max3 Sep '07 - 16:47 
It sounds to be a good contribution and fills up the
void of STL and boost. Thank you!
 
It would be even better if a demo project showing
the basic usage and functionality of it is available.
 
Max
GeneralRe: A need of a demo projectmemberMitchel Haas4 Sep '07 - 0:51 
Hello Max,
 
Thanks for the nice comments on the library.
The documentation download (above, and at http://www.datasoftsolutions.net/tree_container_library/documentation.php) contain examples for every operation in the library, and also examples of using the library with STL containers and algorithms. It also contains four larger example projects, in which a couple of these illustrate real world (although very simple) applications. The documentation (chm file) also contains the actual h/cpp files for the examples, so you can use/compile/run/modify them on your own machine.
Please let me know if you have any problems with any of the examples, and thanks again!
 
Mitchel Haas
GeneralRe: A need of a demo projectmembermin_2_max4 Sep '07 - 14:32 
Hello Mitchel Haas,
 
Thanks for you for your prompt reply.
 
Yes, I have found four example cpp files included in the chm help.
That's good.
 
Still as a suggestion, it would be convenient for the readers/users
of the lib to have them included in a demo solution package that is
seperately downloadable.
 
Thanks so much anyway.
 
Max

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 22 Aug 2007
Article Copyright 2006 by Mitchel Haas
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid