Click here to Skip to main content
Click here to Skip to main content
Go to top

Composites and Visitors - a Templatised Approach

, 13 Oct 2004
Rate this:
Please Sign up or sign in to vote.
An article on a templatised implementation of composite and visitor interaction.

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 <span class="code-keyword"><list>
</span>
#include <span class="code-keyword"><algorithm>
</span>

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 <span class="code-keyword"><Visitor.h>
</span>

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

Share

About the Author

Dave Handley
Web Developer
United Kingdom United Kingdom
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.

Comments and Discussions

 
Generala small remindment of compile error Pinmemberholyburak12-Jan-10 2:58 
GeneralException safe PinmemberMember 38973903-Jan-08 22:59 
JokeVerylpqd good Sitelzmj! PinsussArmspzfl13-Dec-07 22:23 
JokeVerylpqa good Siteevqy! PinsussXowaejfr13-Dec-07 22:20 
JokeVerypkbj good Sitelpqu! PinsussGulcthen13-Dec-07 20:40 
JokeVerydhiv good Sitewaeo! PinsussWnrbcpzs13-Dec-07 20:20 
JokeVeryxfja good Sitemltn! PinsussSzrebhng13-Dec-07 20:01 
JokeVerylpdu good Sitectgh! PinsussYctuenxr13-Dec-07 19:35 
JokeVeryicvj good Sitegkbf! PinsussXbsdjtnc13-Dec-07 19:26 
GeneralTalent Pinmemberwiali14-Jul-05 7:11 
GeneralRe: Talent PinmemberDave Handley14-Jul-05 8:18 
GeneralRe: Talent PinmemberDafydd18-Feb-06 10:49 
Generalgreat! Pinmemberf215-Oct-04 3:04 
GeneralRe: great! PinmemberDave Handley16-Oct-04 23:49 
GeneralNice Pinmemberpete_b1-Oct-04 14:47 
GeneralRe: Nice PinmemberDave_H1-Oct-04 22:12 
GeneralRe: Nice Pinmemberpete_b3-Oct-04 2:34 
GeneralRe: Nice PinmemberDave_H3-Oct-04 3:54 
GeneralRe: Nice PinmemberDave_H6-Oct-04 13:32 
GeneralFew appoints Pinmemberemilio_grv9-Aug-04 22:07 
GeneralRe: Few appoints PinmemberDave_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 appoints Pinmemberemilio_grv10-Aug-04 8:08 
GeneralRe: Few appoints PinmemberDave_H10-Aug-04 8:53 
GeneralRe: Few appoints Pinmemberemilio_grv10-Aug-04 21:19 
GeneralVery inspiring!! PinmemberWREY17-Oct-04 8:03 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

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