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

Composites and Visitors - a Templatised Approach

By , 13 Oct 2004
 

Introduction

This article provides a generic templatised implementation of the composite, including iterators for deep and shallow iteration. It then integrates this composite with a Loki Visitor (see Andrei Alexandrescu's work on the Loki library), detailing different visitation strategies.

Background

The composite is one of my favorite design patterns. It provides a simple implementation of a tree hierarchy. Potential uses include: the scene hierarchy in a graphics system; or the implementation of expression trees. Whilst the composite is a very powerful design pattern and is used in a huge variety of applications, I have not seen any good generic implementations. In approaching this problem, I wanted to deal with many of the key issues with building composites. These issues include memory management, the provision of iterators (both to iterate at a single level in the tree and to iterate over the whole tree), and providing a simple API. In order to achieve these goals, I used the curiously recurring template pattern to provide the programmer with a base class whose interface is easily usable by the inherited class. I have tried to keep dynamic casts to a minimum in the implementation, but have been forced to allow a few in. The deep iterators require 2 dynamic casts, and navigating up the tree requires a dynamic cast.

The implementation of deep and shallow iterators on the composite follows the normal techniques for creating iterators that are compatible with STL containers. The shallow iterator is a bi-directional iterator; whereas the deep iterator is unidirectional. They are not fully STL compliant, because I have not inherited off the STL iterator class, which means that they do not implement iterator traits; however, this addition should be relatively easy if a particular application requires it. The only other area where considerable improvements could be made is that I have implemented the deep iterator to be small, rather than efficient. The deep iterator starts at a particular node, it then moves through all the children, before moving onto siblings. The process of iterating to the next sibling involves searching the entire child list of the parent to get the current position. This could be improved by storing an STL list iterator in the deep iterator class and using it to move to the next sibling. This optimization is left as an exercise, although I will implement it if there is sufficient interest.

The visitor that I have used is the Loki visitor implemented by Alexandrescu. He has created by far the most impressive templatised visitor I have ever come across. At the cost of a single dynamic cast to accept the visitor, he completely decouples the visitor and visitable classes. In doing so, the implementation neatly sidesteps the only really major issue with the visitor pattern as described by Gamma et al. I have expanded on his work in 2 very simple ways. The first point is that if you have a hierarchy of classes, the Loki Visitor will only visit the level at which you specify visitation should occur. By placing some simple code in the base class visitor, you can make the system repeatedly try to visit base classes until it finds one that has been implemented. The second point concerns the integration of the visitor with a composite. Generally, when discussing the visitor pattern, no mention is made of how to visit an entire hierarchy of objects, such as that defined by a composite. By implementing the order of visitation in a number of interface classes, the order of visitation can be easily mixed in to each visitor as required. This leaves a remarkably clean interface where a visitor can visit an entire composite hierarchy using the trivial line of code:

visitor( composite );

Using the code

To implement a generic composite, derive from the CGenericTree class like this:

class CNodeBase : public Types::CGenericTree<CNodeBase>
   {
   virtual CNodeBase *Clone() { return new CNodeBase( *this ); }
   };

In order to make the composite visitable with the Loki visitor, use:

class CNodeBase : public Loki::BaseVisitable<>, 
   public Types::CGenericTree<CNodeBase>
   {
   DEFINE_VISITABLE()
   virtual CNodeBase *Clone() { return new CNodeBase( *this ); }
   };

Examples of the use of the CGenericTree usage can be seen in the NodeBase.h source file. Here, I have created a simple hierarchy of a base class and 2 derived classes, all of them visitable. Note that the implementation of the Clone function is necessary to enable the composite tree copy and equals operators to work properly.

To implement a generic visitor is slightly more complicated. The basic Loki visitor is implemented as follows:

 class CVisitorBase : public Loki::BaseVisitor, 
   public Loki::Visitor<CNodeBase>
   {
   virtual void Visit( CNodeBase &node ) {}
   };

If you look in VisitorBase.h, you will see the base class defined in CVisitorBase. Here, you can see the additional (simple) code necessary to make sure that if we have a type for which the visit function is not implemented in the derived class, a generic base class Visit function can be used. This is demonstrated in CVisitorDerived1. CVisitorDerived2 shows a completely defined visitor.

Now, for the clever part. By mixing in the curiously recurring template from GenericVisitor.h, you can turn your plain vanilla visitor into functors that visit tree hierarchies in a given order. For example:

class CVisitorDerived2TopToBottom :
   public CVisitorDerived2,
   public Types::CVisitorTopToBottom<CVisitorDerived2TopToBottom,CNodeBase>
   {
   };

Note that you do not need to implement any code into this final derived version of the visitor, it is only necessary to mix together the 2 base classes to generate a fully working visitor. The usage of the visitor could not then be easier:

CVisitorDerived2TopToBottom visitor;
CNodeBase *head = ;// Some tree structure
visitor( head );

The Code

For completeness, I have included the code for the composite base class CGenericTree and the 3 mix-in classes for the visitor. I have removed the const functions and iterators, and reformatted the code slightly to make it a little easier for browsing.

#pragma once

#include <list>
#include <algorithm>

namespace Types {

template <typename T>
class CGenericTree
{
public:
    // Forward declarations and friends
    class shallow_iterator;
    class deep_iterator;
    friend class shallow_iterator;
    friend class deep_iterator;
private:
    // Typedefs
    typedef typename T* _tptr;
    typedef typename std::list<T*>::iterator container_iterator;
    typedef typename std::list<T*>::const_iterator const_container_iterator;
public:
    class shallow_iterator
    {
   friend class CGenericTree<T>;
    public:
   // Normal constructors and destructors
   shallow_iterator() :
  m_iterator() {}
   shallow_iterator( const shallow_iterator &src ) :
  m_iterator( src.m_iterator ) {}
   shallow_iterator &operator=( const shallow_iterator &rhs )
   {
  if ( this != &rhs )
  {
      m_iterator = rhs.m_iterator;
  }
  return *this;
   }
   ~shallow_iterator() {}
   // Equality operator
   bool operator==( const shallow_iterator &rhs ) const
   {
  return m_iterator == rhs.m_iterator;
   }
   bool operator!=( const shallow_iterator &rhs ) const
   {
  return m_iterator != rhs.m_iterator;
   }
   // Referencing operators
   _tptr& operator*()
   {
  return *m_iterator;
   }
   _tptr* operator->()
   {
  return  m_iterator.operator->();
   }
   // Increment/decrement operators
   shallow_iterator& operator++()
   {
  ++m_iterator;
  return *this;
   }
   shallow_iterator operator++(int)
   {
  shallow_iterator tmp = *this;
  ++*this;
  return tmp;
   }
   shallow_iterator& operator--()
   {
  --m_iterator;
  return *this;
   }
   shallow_iterator operator--(int)
   {
  shallow_iterator tmp = *this;
  --*this;
  return tmp;
   }
    private:
   // Private constructor off a normal iterator
   explicit shallow_iterator( const container_iterator &iter ) :
  m_iterator( iter ) {}
   // Get the internal storage iterator
   container_iterator GetIterator()
   {
  return m_iterator;
   }
   // Internal storage is a normal iterator
   container_iterator m_iterator;
    };
    class deep_iterator
    {
   friend class CGenericTree<T>;
    public:
   // Normal constructors and destructors
   deep_iterator() :
  m_iterator( NULL ) {}
   deep_iterator( const deep_iterator &src ) :
  m_iterator( src.m_iterator ) {}
   deep_iterator &operator=( const deep_iterator &rhs )
   {
  if ( this != &rhs )
  {
      m_iterator = rhs.m_iterator;
  }
  return *this;
   }
   ~deep_iterator()
   {
  m_iterator = NULL;
   }
   // Equality operator
   bool operator==( const deep_iterator &rhs ) const
   {
  return m_iterator == rhs.m_iterator;
   }
   bool operator!=( const deep_iterator &rhs ) const
   {
  return m_iterator != rhs.m_iterator;
   }
   // Referencing operators
   _tptr& operator*()
   {
  return m_iterator;
   }
   _tptr* operator->()
   {
  return &**this;
   }
   // Increment operators
   deep_iterator& operator++()
   {
  if ( m_iterator ) m_iterator = m_iterator->GetNext();
  return *this;
   }
   deep_iterator operator++(int)
   {
  deep_iterator tmp = *this;
  ++*this;
  return tmp;
   }
    private:
   // Private constructor off a pointer
   explicit deep_iterator( CGenericTree<T> *src ) :
  m_iterator( dynamic_cast<T*> ( src ) ) {}
   // Internal storage is simply a pointer
   T* m_iterator;
    };
private:
    // Helper function for tree-deep iterators
    T* GetNext();
    T* GetNext( CGenericTree<T> *src );

public:
    // Constructors and destructors
    CGenericTree() : m_parent( NULL ) {}
    CGenericTree( const CGenericTree &src );
    CGenericTree &operator=( const CGenericTree &rhs );
    ~CGenericTree() { DeleteAllChildren(); }
    virtual T* Clone() = 0;

private:
    void CopyAllChildren( const CGenericTree &src );
    void DeleteAllChildren();

public:
    // Iterator begin/end functions
    shallow_iterator begin_shallow()
    {
   return shallow_iterator( m_children.begin() );
    }
    shallow_iterator end_shallow()
    {
   return shallow_iterator( m_children.end() );
    }
    deep_iterator begin_deep()
    {
   return deep_iterator( this );
    }
    deep_iterator end_deep()
    {
   if ( m_parent )
  return deep_iterator( m_parent->GetNext( this ) );
   else
  return deep_iterator( NULL );
    }

    // Tree navigation
    T* GetParent();
    unsigned int size() const
    {
   return (unsigned int) m_children.size();
    }
    bool empty() const
    {
   return m_children.empty();
    }

    // Add and remove entries from the tree
    void DeleteFromTree();
    void push_back( T* entry );
    T* remove( T* entry );
    T* remove( shallow_iterator iter );
    T* remove( deep_iterator iter );
    
private:
    // This list of children of this tree element
    std::list<T*> m_children;
    // The parent of this object
    CGenericTree<T>* m_parent;

};

template <typename T>
T*
CGenericTree<T>::GetNext()
{
    if ( m_children.empty() )
    {
   if ( m_parent )
  return m_parent->GetNext( this );
   else
  return NULL;
    }
    return m_children.front();
}

template <typename T>
T*
CGenericTree<T>::GetNext( CGenericTree<T> *src )
{
    T* entry = dynamic_cast<T*> ( src );
    container_iterator iter = ++( find( m_children.begin(), 
      m_children.end(), entry ) );
    if ( iter != m_children.end() )
   return *iter;
    if ( m_parent )
   return m_parent->GetNext( this );
    return NULL;
}

template <typename T>
CGenericTree<T>::CGenericTree( const CGenericTree<T> &src ) : m_parent( NULL )
{
    CopyAllChildren( src );
}

template <typename T>
CGenericTree<T> &
CGenericTree<T>::operator=( const CGenericTree<T> &rhs )
{
    if ( this != &rhs )
    {
   DeleteAllChildren();
   m_parent = NULL;
   CopyAllChildren( rhs );
    }
    return *this;
}

template <typename T>
void
CGenericTree<T>::CopyAllChildren( const CGenericTree<T> &src )
{
    const_container_iterator iter = src.m_children.begin();
    for ( ; iter != src.m_children.end(); ++iter )
   push_back( (*iter)->Clone() );
}

template <typename T>
void
CGenericTree<T>::DeleteAllChildren()
{
    container_iterator iter = m_children.begin();
    for ( ; iter != m_children.end(); ++iter )
   delete (*iter);
    m_children.clear();
}

template <typename T>
void
CGenericTree<T>::DeleteFromTree()
{
    if ( m_parent )
   m_parent->remove( (T*) this );
    delete this;
}

template <typename T>
T*
CGenericTree<T>::GetParent()
{
    return dynamic_cast<T*> (m_parent);
}

template <typename T>
void
CGenericTree<T>::push_back( T* entry )
{
    m_children.push_back( entry );
    entry->m_parent = this;
}

template <typename T>
T*
CGenericTree<T>::remove( T* entry )
{
    container_iterator iter = find( m_children.begin(), m_children.end(), 
      entry );
    if ( iter != m_children.end() )
   m_children.erase( iter );
    entry->m_parent = NULL;
    return entry;
}

template <typename T>
T*
CGenericTree<T>::remove( shallow_iterator iter )
{
    if ( iter != end_shallow() )
   m_children.erase( iter.GetIterator() );
    (*iter)->m_parent = NULL;
    return *iter;
}

template <typename T>
T*
CGenericTree<T>::remove( deep_iterator iter )
{
    if ( (*iter)->m_parent )
   (*iter)->m_parent->remove( (*iter) );
    return *iter;
}

}
#pragma once

#include <Visitor.h>

namespace Types {

template <typename Visitor, typename BaseVisitable>
class CVisitorTopToBottom
{
public:
    virtual ~CVisitorTopToBottom() {}
    void operator()( BaseVisitable * ); 
};

template <typename Visitor, typename BaseVisitable>
class CVisitorBottomToTop
{
public:
    virtual ~CVisitorBottomToTop() {}
    void operator()( BaseVisitable * ); 
};

template <typename Visitor, typename BaseVisitable>
class CVisitorLeftToRight
{
public:
    virtual ~CVisitorLeftToRight() {}
    void operator()( BaseVisitable * );
};

template <typename Visitor, typename BaseVisitable>
void
CVisitorTopToBottom<Visitor, BaseVisitable>::operator()( 
          BaseVisitable *visitable )
{
    // Visit this
    visitable->Accept( *dynamic_cast<Visitor*>( this ) );
    // Now visit all the children
    if ( visitable->empty() )
   return;
    BaseVisitable::shallow_iterator iter = visitable->begin_shallow();
    for ( ; iter != visitable->end_shallow(); ++iter )
   (*this)(*iter);
}

template <typename Visitor, typename BaseVisitable>
void
CVisitorBottomToTop<Visitor, BaseVisitable>::operator()( 
           BaseVisitable *visitable )
{
    // Visit all the children
    if ( !visitable->empty() )
    {
   BaseVisitable::shallow_iterator iter = visitable->begin_shallow();
   for ( ; iter != visitable->end_shallow(); ++iter )
  (*this)(*iter);
    }
    // Now visit this
    visitable->Accept( *dynamic_cast<Visitor*>( this ) );
}

template <typename Visitor, typename BaseVisitable>
void
CVisitorLeftToRight<Visitor, BaseVisitable>::operator()( 
         BaseVisitable *visitable )
{
    // Visit the left hand child
    if ( !visitable->empty() )
   (*this)(*visitable->begin_shallow());
    // Visit this
    visitable->Accept( *dynamic_cast<Visitor*>( this ) );
    // Visit the right hand child(ren)
    if ( visitable->size() > 1 )
    {
   BaseVisitable::shallow_iterator iter = ++visitable->begin_shallow();
   for ( ; iter != visitable->end_shallow(); ++iter )
  (*this)(*iter);
    }
}

}

Points of Interest

There are a huge number of points of interest in this article, I'll pick out a few that I think are quite neat.

The curiously recurring template pattern allows you to give information about derived classes to a generic base class. This can be used in very simple ways, such as implementing a generic reference counting mechanism (the usual book example). I've used the pattern twice. The first time allowed me to make the interface of CGenericTree into something usable by the base class, without the programmer constantly worrying about dynamic casts. The second time was used to implement the dispatch from the mix-in strategy for the visitor, allowing me to dispatch the visitable objects to the visitor without knowing anything about the final implementation of the visitor.

Implementing STL style iterators is easy and incredibly useful. The 2 iterators used in CGenericTree can be fed into STL algorithms (e.g., find) without thinking about it. The main reason that I believe people don't implement iterators regularly is the occasionally difficult syntax. My advice is that if you are stuck, look at someone else's implementation. I regularly browse the Microsoft list implementation for inspiration. An example of tricky syntax which takes a bit of getting used to is:

const _ctptr* operator->() const { return &**this; }

Loki is an excellent library. There are other articles on CodeProject concerning, for example, Singletons, Factories, or Multiple Dispatch. My advice on all of these is to buy Alexandrescu's book and use the Loki library.

The generic tree was implemented using normal C++ pointers, another implementation is possible using Boost smart pointers. I've done this once before, and it works really well, just note that there are some implementation issues. For example, the parent pointer must be a weak_ptr, whereas the storage must use a shared_ptr. Also, a weak_ptr cannot be created during construction, so if you want composite nodes to add themselves into a tree automatically, you have to use some sort of factory.

Multiple inheritance can be your friend, just be careful how you use it. I learnt about how good multiple inheritance can be by using the Loki visitor. The only thing that I would recommend steering well clear of in multiple inheritance is the diamond. Generally, I use multiple inheritance to mix-in a new feature into a class, or as an interface. In either case, it is a very powerful tool and not something to be scared of. In this article, multiple inheritance has been used in a wide variety of places. It is used to make an object both a member of a composite tree, and to be visitable by a Loki visitor. It is a critical feature of the Loki Visitor, using a small base class for each visitable type. Finally, it allows a Loki visitor to implement a variety of different strategies for visiting a composite tree.

Finally, I recommend having a look at the ++ operator in the deep iterator along with the GetNext() functions. These functions implement the best way to get across all tree elements (in my opinion), without storing any state other than where you are at present. GetNext() is used to do one of two things. If we have any children, then go down to the first child in the list. Otherwise, recursively go to the parent's next sibling using GetNext(this). This function looks for this in the child list, when it is found it returns the next child; if it is the last child, it goes up to the next parent to do a GetNext(this). When we have run out of parents, we use NULL to signify end(). Note that the implementation of the end_deep() function is not quite as trivial as returning NULL, since we allow deep iterations to start and end at a node other than the root node. The end_deep() function is therefore implemented to return what the value of an increment would be if we were doing a full tree iteration. This can either be NULL or the first node outside the desired iteration range.

Post Script

I have never quite understood the paranoia amongst many C++ programmers about using RTTI. The overhead it adds to an application is tiny, and its uses are almost endless; nevertheless, it seems that there are still people out there who don't like the idea of a few tens of kB of extra memory footprint. As such, I've written a version of the CGenericTree class that static casts up the inheritance hierarchy rather than dynamic casts down. Even though this version is likely to be slightly faster, and doesn't require RTTI, I would recommend using the first version included in the main project. The reason for this is that a maintenance programmer years down tracking could make the mistake of not inheriting from the CGenericTree and try to use it as a normal container. In those circumstances, the static casts will fail catastrophically, whereas the dynamic casts would only return NULL. It's marginal, but I would argue that the latter is easier to debug.

History

  • 8 Aug 04 - Initial release.
  • 10 Aug 04 - Additional file added for no dynamic casts generic tree.
  • 10 Oct 04 - Amended templates to resolve crash in remove functions.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Dave Handley
Web Developer
United Kingdom United Kingdom
Member
I started programming on 8 bit machines as a teenager, writing my first compiled programming language before I was 16. I went on to study Engineering and Computer Science at Oxford University, getting a first and the University Prize for the best results in Computer Science. Since then I have worked in a variety of roles, involving systems management and development management on a wide variety of platforms. Now I manage a software development company producing CAD software for Windows using C++.
 
My 3 favourite reference books are: Design Patterns, Gamma et al; The C++ Standard Library, Josuttis; and Computer Graphics, Foley et al.
 
Outside computers, I am also the drummer in a band, The Unbelievers and we have just released our first album. I am a pretty good juggler and close up magician, and in my more insane past, I have cycled from Spain to Eastern Turkey, and cycled across the Namib desert.

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   
Generala small remindment of compile errormemberholyburak12 Jan '10 - 2:58 
change DEFINE_VISITABLE to LOKI_DEFINE_VISITABLE otherwise you will get some compile errors. thanks for the great article Thumbs Up | :thumbsup:
GeneralException safememberMember 38973903 Jan '08 - 22:59 
Nice article although I would rather use a strategy pattern for depth/width first run of the tree.
 
This is not the issue here but your class is not exception safe in the case of copy assignment.
template <typename T>
CGenericTree<T> & CGenericTree<T>::operator=( const CGenericTree<T> &rhs )
{
    if ( this != &rhs )
    {
   DeleteAllChildren();
   m_parent = NULL;
   CopyAllChildren( rhs );//if throwing here - children are lost and parent is messed up
    }
    return *this;
}
 
The usual idiom is to swap the containers with a local:
template <typename T>
CGenericTree<T> & CGenericTree<T>::operator=( const CGenericTree<T> &rhs )
{
    //no need to test for ( this != &rhs ) because it is safe here and doesn't happen often
    CGenericTree<T> tmp(rhs); //build copy
    m_children.swap(tmp.m_children);//swap containers
    m_parent = NULL;                //adjust parent
    return *this;
}
 

DeleteAllChildren() could also be exposed as clear() which is more idiomatic for containers and you could also typedef value_type for use in back inserters since you already have push_back().
 
Michael
JokeVerylpqd good Sitelzmj!sussArmspzfl13 Dec '07 - 22:23 
http://www.magictghbluepill.com/site1/ [url=http://www.magicfwobluepill.com/site2/]site2[/url] <a href="http://www.magiciznbluepill.com/site3/">site3</a> <a href="http://www.magiclghbluepill.com/site4/">site4</a> [url="http://www.magicytubluepill.com/site5/"]site5[/url] [LINK http://www.magicaevbluepill.com/site6/]site6[/LINK] kbtgq
http://www.magictghbluepill.com/site1/ [url=http://www.magicfwobluepill.com/site2/]site2[/url] <a href="http://www.magiciznbluepill.com/site3/">site3</a> <a href="http://www.magiclghbluepill.com/site4/">site4</a> [url="http://www.magicytubluepill.com/site5/"]site5[/url] [LINK http://www.magicaevbluepill.com/site6/]site6[/LINK] kbtgq
JokeVerylpqa good Siteevqy!sussXowaejfr13 Dec '07 - 22:20 
http://www.magicwaobluepill.com/site1/ [url=http://www.magicsjxbluepill.com/site2/]site2[/url] <a href="http://www.magicimnbluepill.com/site3/">site3</a> <a href="http://www.magicctjbluepill.com/site4/">site4</a> [url="http://www.magichpfbluepill.com/site5/"]site5[/url] [LINK http://www.magiclcdbluepill.com/site6/]site6[/LINK] bwkgq
http://www.magicwaobluepill.com/site1/ [url=http://www.magicsjxbluepill.com/site2/]site2[/url] <a href="http://www.magicimnbluepill.com/site3/">site3</a> <a href="http://www.magicctjbluepill.com/site4/">site4</a> [url="http://www.magichpfbluepill.com/site5/"]site5[/url] [LINK http://www.magiclcdbluepill.com/site6/]site6[/LINK] bwkgq
JokeVerypkbj good Sitelpqu!sussGulcthen13 Dec '07 - 20:40 
http://www.magicycgbluepill.com/site1/ [url=http://www.magicojnbluepill.com/site2/]site2[/url] <a href="http://www.magicgkybluepill.com/site3/">site3</a> <a href="http://www.magicrzubluepill.com/site4/">site4</a> [url="http://www.magicrypbluepill.com/site5/"]site5[/url] [LINK http://www.magicinrbluepill.com/site6/]site6[/LINK] ofjzd
http://www.magicycgbluepill.com/site1/ [url=http://www.magicojnbluepill.com/site2/]site2[/url] <a href="http://www.magicgkybluepill.com/site3/">site3</a> <a href="http://www.magicrzubluepill.com/site4/">site4</a> [url="http://www.magicrypbluepill.com/site5/"]site5[/url] [LINK http://www.magicinrbluepill.com/site6/]site6[/LINK] ofjzd
JokeVerydhiv good Sitewaeo!sussWnrbcpzs13 Dec '07 - 20:20 
http://www.magichycbluepill.com/site1/ [url=http://www.magicfwabluepill.com/site2/]site2[/url] <a href="http://www.magicmhubluepill.com/site3/">site3</a> <a href="http://www.magicjtkbluepill.com/site4/">site4</a> [url="http://www.magicwrmbluepill.com/site5/"]site5[/url] [LINK http://www.magicbjkbluepill.com/site6/]site6[/LINK] qhlyi
http://www.magichycbluepill.com/site1/ [url=http://www.magicfwabluepill.com/site2/]site2[/url] <a href="http://www.magicmhubluepill.com/site3/">site3</a> <a href="http://www.magicjtkbluepill.com/site4/">site4</a> [url="http://www.magicwrmbluepill.com/site5/"]site5[/url] [LINK http://www.magicbjkbluepill.com/site6/]site6[/LINK] qhlyi
JokeVeryxfja good Sitemltn!sussSzrebhng13 Dec '07 - 20:01 
http://www.magickfjbluepill.com/site1/ [url=http://www.magicctybluepill.com/site2/]site2[/url] <a href="http://www.magicevmbluepill.com/site3/">site3</a> <a href="http://www.magickfjbluepill.com/site4/">site4</a> [url="http://www.magicmqhbluepill.com/site5/"]site5[/url] [LINK http://www.magicfcubluepill.com/site6/]site6[/LINK] evzmj
http://www.magickfjbluepill.com/site1/ [url=http://www.magicctybluepill.com/site2/]site2[/url] <a href="http://www.magicevmbluepill.com/site3/">site3</a> <a href="http://www.magickfjbluepill.com/site4/">site4</a> [url="http://www.magicmqhbluepill.com/site5/"]site5[/url] [LINK http://www.magicfcubluepill.com/site6/]site6[/LINK] evzmj
JokeVerylpdu good Sitectgh!sussYctuenxr13 Dec '07 - 19:35 
http://www.magictkbbluepill.com/site1/ [url=http://www.magicaribluepill.com/site2/]site2[/url] <a href="http://www.magicdhubluepill.com/site3/">site3</a> <a href="http://www.magictbsbluepill.com/site4/">site4</a> [url="http://www.magicypgbluepill.com/site5/"]site5[/url] [LINK http://www.magicsnebluepill.com/site6/]site6[/LINK] ulpmw
http://www.magictkbbluepill.com/site1/ [url=http://www.magicaribluepill.com/site2/]site2[/url] <a href="http://www.magicdhubluepill.com/site3/">site3</a> <a href="http://www.magictbsbluepill.com/site4/">site4</a> [url="http://www.magicypgbluepill.com/site5/"]site5[/url] [LINK http://www.magicsnebluepill.com/site6/]site6[/LINK] ulpmw
JokeVeryicvj good Sitegkbf!sussXbsdjtnc13 Dec '07 - 19:26 
http://www.magicolybluepill.com/site1/ [url=http://www.magictopbluepill.com/site2/]site2[/url] <a href="http://www.magicnejbluepill.com/site3/">site3</a> <a href="http://www.magicupxbluepill.com/site4/">site4</a> [url="http://www.magicduybluepill.com/site5/"]site5[/url] [LINK http://www.magickbfbluepill.com/site6/]site6[/LINK] rmdqa
http://www.magicolybluepill.com/site1/ [url=http://www.magictopbluepill.com/site2/]site2[/url] <a href="http://www.magicnejbluepill.com/site3/">site3</a> <a href="http://www.magicupxbluepill.com/site4/">site4</a> [url="http://www.magicduybluepill.com/site5/"]site5[/url] [LINK http://www.magickbfbluepill.com/site6/]site6[/LINK] rmdqa
GeneralTalentmemberwiali14 Jul '05 - 7:11 
Great job, I think this article can be added into the book of <<Mordern c++ design>>,
Obviously, the using of c++ and design pattern you just had created is an art!
 
coding is cooking
GeneralRe: TalentmemberDave Handley14 Jul '05 - 8:18 
Thanks,
 
I've been working on and off for the past few months on tidying up this code for potential submission to boost
GeneralRe: TalentmemberDafydd18 Feb '06 - 10:49 
Agree with wiali. Mr. Handley, great article, I'll buy a revised version of the book if you becoming a contributing author.
 
Separately, and the subject topic remains appropriate, I wonder if Kasper Peeters could be persuaded to revise the licence terms of his stl-like tree, which he is still maintaining - at the Max Planck institute no less. I used to code commercially and feel it is a pity if academics restrict their work to, essentially, other academics, especially if they are in part supported by income tax.

 
Dafydd
Life is short so make the most of it, and try not to shorten anyone else's along the way.
Generalgreat!memberf215 Oct '04 - 3:04 
Big Grin | :-D
 

 
from,
-= aLbert =-

GeneralRe: great!memberDave Handley16 Oct '04 - 23:49 
Thanks,
 
Dave
GeneralNicememberpete_b1 Oct '04 - 14:47 
Nice and lightweight - I'll be using something very similar to this that I've got in mind. Thanks for an interesting article!
 
Only grizzles I'd have are:
 
I hate classes that are prefixed with 'C' Poke tongue | ;-P . Surely that should be reserved for mfc library classes? Isn't the point of classes prefixed with 'C' to prevent name clashes with libraries in the pre-namespace days?
 
'Types' is also a pretty crap name for a namespace. I'd have given it a more complex name, and then renamed the namespace to something shorter for specific projects.
 
They are purely academic grizzles though, and don't affect the actual function of the classes Wink | ;)

GeneralRe: NicememberDave_H1 Oct '04 - 22:12 
Thanks for the compliments.
 
I agree that the names are not great - but I tend to find naming standard are so personal anyway, and with a class this size I would probably expect people to tweak it and move things around. The only reason I named things as they are, are simply because we name classes with a prefix C at work, and all of our support classes are inside a Types namespace that is inside our product namespace.
 
Feel free to hack around the code as much as you want - please let me know if you come up with any interesting additions etc.
 
Dave
GeneralRe: Nicememberpete_b3 Oct '04 - 2:34 
Hi there,
 
I've been hacking around a bit, and came across a couple of things that I felt compelled to change (apart from names etc).   Not particularly interesting additions, but perhaps the first is important in the batlle against undefined behaviour.
 
The destructor did not really behave in the way I wanted it to.   On deleting a node, it does not get removed from the parents' children list and you end up with a dangler.   Ideally for my application, I wanted to just be able to delete a node and need do nothing else to ensure that the tree is correctly maintained.
 
So, the destructor would become:
 
~CGenericTree() {
     if (m_parent) {
          m_parent->remove(static_cast<T*>(this));
     }
     DeleteAllChildren();
}
 
The necessitates a change in DeleteAllChildren() to prevent problems with an item trying to remove itself from a parent that is in the process of removing its children - only   the first item that is being deleted should remove itself from its parent, and then handle deleting all of its children and subsequent list erasure.
 
DeleteAllChildren()
{
     container_iterator iter = m_children.begin();
     for ( ; iter != m_children.end(); ++iter ) {
          (*iter)->m_parent = 0; // added
          delete (*iter);
     }
     m_children.clear();
}
 
This all makes the DeleteFromTree() function extraneous, and as such it may as well be removed.
 

In fact, you could leave the DeleteAllChildren function untouched (setting the poaent to null is unneccesary in some cases) and copy the code into the destructor without the unneccesary m_children.clear();.  
 
In my version it is as follows:
 
template <class T>
Composite<T>::~Composite()
{    
     if (parent_) {
          parent_->remove(static_cast<T*>(this));
     }
 
     shallow_iterator it = shallowBegin();
 
     for (; it != shallowEnd(); ++it) {
          (*it)->parent_ = 0;
          delete (*it);
     }
}
 
-=/=-
 
I ended up adding a constructor that accepts a T* parameter as a parent, so that you can easily build up a tree structure when creating objects.
 
// declaration in the class
     explicit Composite(T* parent);
 
// definition
template <class T>
inline
Composite<T>::Composite(T* parent)
: parent_(parent)
{
     if (parent) {
          parent->push_back(static_cast<T*>(this));
     }
}
 
-=/=-
 
The assignment operator does this:
 
          DeleteAllChildren();
          m_parent = NULL;
          CopyAllChildren( rhs );
 
Admittedly, setting the parent to null prevents the hideous problems you'd have with a circular tree.   This isn't the behaviour that I'd expected though.   I was thinking perhaps that the natural expectation would be that you could insert a sub tree into an existing tree structure this way, without the parent association being affected.   This poses problems I've not tackled yet, but I thought I'd see if you had any comments   on it.
 
-=/=-
 
The shallow_iterator class seemed to me to be totally extraneous.   Would not a typedef have had exactly the same effect and saved many lines of code? :
 
typedef container_iterator shallow_iterator;
typedef const_container_iterator const_shallow_iterator;
 
In fact, the container_iterator becomes unneccesary and can also be removed.   Any iteration of the children can be accomplished in terms of the shallow_iterator.   The class member functions for begin_shallow etc can also be used to simplify the syntax in a couple of places.
 

-=/=-
 
Other than renaming a number of things, I ended up defining the deep iterators outside of the class and adding typedefs into the class to make things more readable - I found it difficult to see the full CGenericTree interface at a glance.   Just a matter of personal style though.
 

GeneralRe: NicememberDave_H3 Oct '04 - 3:54 
Pete,
 
I'm not surprised that you changed the destructor to automatically remove from the tree. I've written alot of composites in my time, and used both methods. There are a few reasons why I went for the DeleteFromTree explicit method rather than using the Destructor. The first is the iterative problem in DeleteAllChildren - I've had nightmares with this in other applications and wanted to avoid it completely. Another reason is tied in with Serialisation frameworks. Serialisation is an issue I completely avoided in my article, but one lesson I have learnt about serialising composites, is that, if you want the entire composite tree to serialise or de-serialise itself automatically, the default constructors and destructors have to be completely clean - with no extraneous functionality. A similar issue applies if you use the framework as part of an OO middleware layer for RDBMS access. Despite these reasons, it is perfectly possible to code the system both ways, and both ways are equally valid. I think it is just personal choice which you use.
 
Regarding having a constructor from a T* parent, I personally decided not to do this because of possible confusion with the copy constructor. Again, I think this is a personal choice, but I decided to keep the syntax for adding a new child as a parent operation rather than the other way round. Hence, you may have:
new MyClass( parent );
whereas I would have
parent->push_back( new MyClass );
I think both are fine - they are both clean and easy to understand. But my preference is with the latter. Mainly because it gives you the opportunity to use a non-default constructor of your container object, eg:
parent->push_back( new MyClass( myData ) );
 
The assignment operator and copy constructor in composites are always difficult issues. Do you also want to assign the position in the tree or not. There is a strong argument to deal with the issue in precisely the same way that I dealt with the problem of whether to delete the parent's entry in the destructor or not. Under these circumstances your assignment operator and copy constructor would operate as I've coded them, and you would have a CopyTree type of function that would actually copy position as well, dealing with cyclic trees through a CheckIntegrity function that checks for recursive trees. My personal preference is that to achieve a = b, you would do something like this:
b.GetParent()->push_back( &( a = b ) );
 
Because I used a std::list as my underlying implementation of child list storage, the shallow_iterator does seem a little pointless. There are; however, a few reasons to keep the code as is. Firstly, the shallow_iterator decouples the iterator from the storage model, and allows you to use other methods for storage as required. If you wanted you could make the underlying storage a trait without too much difficulty. It also makes it easier to write functionality to convert a deep_iterator into a shallow_iterator and vice versa. Some people would argue that I have fallen into the trap of speculative generality, but I think under these circumstances it makes at least a little sense.
 
Whether you put the iterators inside or outside the class is really a matter of choice. I personally like all my iterators inside the class, in the same way as iterators in the STL, but that doesn't mean moving them outside is wrong, so long as they are appropriately namespaced.
 
Finally, if you want to send me your finished code when you are happy with it, I'd quite like to add it to the article so that other people could make use of your changes. Whether I agree or disagree with all your ideas, I think they are all tremendously useful for helping people learn about composites. In my view, Composites and Visitors are 2 of the most useful design patterns, and 2 of the hardest to actually get working correctly!
 
Dave
GeneralRe: NicememberDave_H6 Oct '04 - 13:32 
Pete,
 
I've just put up another article on typed iterators that may be of interest to you if you are still working with composites. The article can be found at http://www.codeproject.com/vcpp/stl/typed_iteration.asp[^].
 
I'm also planning another article concerning passing functors into iterators to change the iterator behaviour - I'll post again when it is up.
 
Dave
GeneralFew appointsmemberemilio_grv9 Aug '04 - 22:07 
I think this is a typical sample of "strange" way to do simple things.
First: Let me say that what I think of Alexandrescu is that he's mostly doing ... menatl masturbations. I know many people don't agree, but that's what I think. Behind nice ideas, there is a too generalized way to implement, with the risk to create lot of ovehead.
 
Use your class 2, 3 ,4 ... 10 times with different classes and you will create in your programe 10 times the required code.
 
I don't really understand this enthusiasm behind the "curiously recurring template" that many people - alexandrescu as first- seems manifest around it. Just to avoid a language feature like virtual functions ?
 
Templates, in my mind, are good to create algoritms or data structure around generic data, non originally thought for that functionality. That's like an std::list<UINT> or std::list<something>;. There is nothing in UINT or "something" that make them "listable".
 
But if you start with the idea that a mycalss must be derived from a supplied base expecting certain function to be implemented in myclass ... I think that traditional virtual functions and OOP are superior: the (binary) code of the base will be just one.
 
Neverless, if you require dynamic_cast-s: it not a problem of few or many, but of yes or not. If you use it, yuou need RTTI. And all the internal data structures it carries. At that point, the overhead of a v-table ... it's just a part of the game !
 

 

2 bugs found.
> recompile ...
65534 bugs found.
D'Oh! | :doh:

GeneralRe: Few appointsmemberDave_H10 Aug '04 - 6:07 
I think you've missed the whole point of the composite design pattern. To quote from Gamma et al, "A simple implementation could define class for ... primitives ... plus other class that act as containers for these primitives." This is the whole concept of std::list or similar. But the Design Patterns book goes on to say "But there's a problem with this approach: Code that uses these classes must treat primitive and container objects differently, even if most of the time the user treats them identically."
 
I don't think a composite design pattern should replace a tree container object - it is another tool to place in your bag of design tools and use when you need it. I find the composite pattern most useful when I have a tree structure where individual objects need to know about there own parents or children. This knowledge is very difficult to implement if we use normal STL type containers. Where this becomes useful is detailed in a couple of my examples. A graphics system may implement shapes as a leaf node, and groups of shapes as a composite node. Calculating the bounding box on a node is then done through the same virtual function call on both leaf and composite nodes. But the composite node knows about it's place in the structure and can calculate the bounding box by merging all the child bounding boxes. A similar example is expression trees, where you can ask any expression to calculate itself, composite expressions need to know about their own children in order to calculate the expression - knowledge that would simply not be there if they were stored in an abstract container. If you implement the generic tree as a simple (non-templatised) base class, like you advocate, then the child can only know about the structure of the tree through the interface of the base class. In order to call a bounding box or expression evaluation virtual function, you would need to dynamic_cast down to your specific base, or reimplement all the tree functions in every composite you ever implemented. Using a template means that the interface is simple, and if you work it out, the compiler will generate no more code than if you implemented the same functionality with "traditional virtual functions and OOP" as you advocate.
 
Your objection that every time you use this base class on a class you are injecting a whole set of code, is just the same as the use of the STL containers. Every time you instantiate a new std::list with a different type you are injecting a whole load of new code. It's worth it because at least you aren't writing the same code again and again. The number of times I have had to implement tree shallow and deep iterators led me to write the template in this article.
 
Regarding my use of dynamic_casts, I fully understand the issue regarding the overhead of dynamic_casts in terms of having to enable RTTI, but this issue is routinely overstated. Each type of class has to carry around a single pointer that says what type it is added to the vtable, only for virtual classes that already have the vtable in them anyway. Furthermore, for each individual type we have to have a small lightweight object, but there is only one of those per class type, rather than instance. In the worst case, with hundreds of polymorpic classes in the system, each with hundreds of virtual functions, you have an increase of memory footprint of a few 10s of kBs. It's a small price to pay for a great language feature.
 
Finally, whether you like or dislike Alexandrescu's work is a personal matter, but I have found it incredibly useful. I work on a huge CAD system, where you have a large number of system objects stored in a tree structure, these system objects are of a whole number of different types. There are then actions in the system that can apply to the tree structure. I wanted to implement these as the visitor because it is the most obvious pattern to use, unfortunately, there is an unplanned overhead of doing this. If I want to add a new object type into the system - something that should be easy with OO - I add this object type to the main tree, inheriting off the base type. In turn, I have to include a new function in the base visitor that will visit this new type. Then I implement the visit function in whichever derived visitors I need to. Then I go to compile and find that the dependencies between the visitor and the visited mean I have to recompile most of my main system objects, and the entire visitor hierarchy (the base header has changed). The cost - a few hours of compilation time. Using Alexandrescu's visitor and the only functions that need recompiling are the new object and the visitors that I have had to edit. The cost - a single dynamic_cast to perform the visit. In my CAD system, the overhead of the Loki visitor framework has never appeared in the top 99.99% of CPU usage on any profile of the system we have ever taken under any circumstances. It has however, helped to increase the work that my team can do by a significant percentage.
 
As a final by the way, the curiously recurring template pattern is not there to avoid the use of virtual functions. It is there to allow a fully generic base class to know some information about classes deriving from it. As an example, I would like to see a generic way of object counting instances of an object using a base class that didn't use the CRTP. The reason I don't think it is possible (and I may be wrong), is because in order to object count, you must store a static variable containing your number of objects. If you don't know about the classes derived off you, then you don't know what type of object has just been constructed or destructed, you only know that construction or destruction has taken place.
 
Dave H
GeneralRe: Few appointsmemberemilio_grv10 Aug '04 - 8:08 
uhmm ... not shure if you really undesrtand my points.
I'm not talking in term of "replacements", but in term of usability and compresibility of the resulting code.
 
Dave_H wrote:
I find the composite pattern most useful when I have a tree structure where individual objects need to know about there own parents or children.
 
True: but come to our case: you use dynamic_cast, so your object have RTTI descriptor and v-tables.
This kind of knowelwdge can be obtained also by inheriting from a common ETreenode, (a ITreeNode that provide an implementation in therm of pointers and access- modifiers functions.
 
Dave_H wrote:
If you implement the generic tree as a simple (non-templatised) base class, like you advocate, then the child can only know about the structure of the tree through the interface of the base class. In order to call a bounding box or expression evaluation virtual function, you would need to dynamic_cast down to your specific base
 
Exactly: but this is equivalent as casting the base to the derived class using CRTP.
You can find samples in http://www.codeproject.com/gen/design/codestyling_eg_2.asp[^]
 

 

2 bugs found.
> recompile ...
65534 bugs found.
D'Oh! | :doh:

GeneralRe: Few appointsmemberDave_H10 Aug '04 - 8:53 
emilio_grv wrote:
you use dynamic_cast, so your object have RTTI descriptor and v-tables
 
The v-table is a result of the class structure being virtual - the same with both inheritence hierarchies and the templatised composite I have shown in the article. The only addition in the case of RTTI is that each polymorphic type has to have a type_info structure, and we have one extra pointer in the v-table. In most implementations of C++ this means an extra 30-40 bytes per type of class. In my experience, I have found that composite hierarchies tend to have many orders of magnitude less types than objects so this overhead is minimal.
 
emilio_grv wrote:
but this is equivalent as casting the base to the derived class using CRTP
 
Unfortunately, I think you have missed the key difference. This is that when you use the CRTP the number of casts are minimised primarily because the data is stored as the root of the composite hierarchy rather than the abstract composite interface implemented in CGenericTree. This has the advantage of making shallow iteration about the same speed as iterating over a normal std::list. Also, when you do need to cast, this is done in the CGenericTree rather than the base of your composite hierarchy, meaning that you don't have to think about the casting. Therefore, it provides a simpler interface.
 
If you have an implementation of a composite that doesn't use CRTP then I would be interested in seeing it since my main motivation for writing these classes was simply that I have written composite deep and shallow iterators far too many times, and wanted a generic way of implementing them.
 


 
Dave H
GeneralRe: Few appointsmemberemilio_grv10 Aug '04 - 21:19 
Dave_H wrote:
If you have an implementation of a composite that doesn't use CRTP then I would be interested in seeing it since my main motivation for writing these classes was simply that I have written composite deep and shallow iterators far too many times, and wanted a generic way of implementing them.
 
You gave me an idea for an aticle !
I think I'll propose it: just the time to code Wink | ;)
 

2 bugs found.
> recompile ...
65534 bugs found.
D'Oh! | :doh:

GeneralVery inspiring!!memberWREY17 Oct '04 - 8:03 
I couldn't help but reread several portions of your very valuable comments because there seems to be a great deal of similarities of what you say about this pattern, and the "Reflection" pattern (which I'm currently studying in-depth).
 
Because of limited experience in my use of the Composite-Visitor pattern, I would like to ask your opinion about the use and preference of choosing it over the "Reflection" pattern (or vice-versa).
 
For example, both patterns espouses a degree of self-awareness, except for me, I see the "Reflection" pattern as more universally applied (meaning, its wings are pretty widespread). It is more extendable and enormously comprehensive in that the "Reflection" pattern makes it its business to know (right down to the level of depth), what functions are responsible for what behavior in the system, greater than what the "Composite-Visitor" pattern seems to offer. This it accomplishes through two levels, "A meta level," and a "base level", with the 'meta' level comprised of 'meta objects' that acts as (sort of) guardians and overseers of what occurs in the base level.
 
Reason why the "Reflection pattern is so comprehensive, is that it offers something of a "surgically" precise means to make changes to the system with full knowledge of knowing what parts would be affected.
 
Can you share a word or two on this point of differences between these two patterns? Thank you!
 
Big Grin | :-D
 
William
 
Fortes in fide et opere!

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 14 Oct 2004
Article Copyright 2004 by Dave Handley
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid