WIP: pondering diff representation variants

Actually I arried at the conclusion, that the *receiving* of
a diff representation is actually a typical double-dispatch situation.
This leads to the attempt to come up with a specialised visitor
as standard pattern to handle and apply a diff. Obviously,
we do not want the classical GoF-Visitor, but (yes, we had
that discussion allready) -- well in terms of runtime cost,
we have to deal with at least two indirections anyway;
so now I'm exploring the idea to implement one of these
indirections through a functor object, which at the same time
acts as "Tag" in the diff representation language (instead
of using an enum as tag)
This commit is contained in:
Fischlurch 2014-11-10 04:00:39 +01:00
parent ed54f44b5e
commit 09e7e1f8f5
11 changed files with 266 additions and 88 deletions

View file

@ -118,8 +118,8 @@ namespace lib {
/**
* Adapter for building an implementation of the lumiera forward iterator concept.
* The "current position" is represented as an opaque element (usually an nested iterator),
* with callbacks to the controlling container instance for managing this position.
* The "current position" is represented as an opaque element (usually a nested iterator),
* with callbacks into the controlling container instance to manage this position.
* This allows to influence and customise the iteration process to a large extent.
* Basically such an IterAdapter behaves like the similar concept from STL, but
* - it is not just a disguised pointer (meaning, it's more expensive)
@ -262,10 +262,10 @@ namespace lib {
/**
* Another Lumiera Forward Iterator building block, based on incorporating a state type
* right into the iterator. Contrast this to IterAdapter referring to an controlling
* right into the iterator. Contrast this to IterAdapter, which refers to a managing
* container behind the scenes. Here, all of the state is assumed to live in the
* custom type embedded into this iterator, accessed and manipulated through
* a set of free function to be resolved by ADL.
* a set of free functions, picked up through ADL.
*
* \par Assumptions when building iterators based on IterStateWrapper
* There is a custom state representation type ST.
@ -273,7 +273,7 @@ namespace lib {
* - this default state represents the \em bottom (invalid) state.
* - copyable, because iterators are passed by value
* - this type needs to provide an <b>iteration control API</b> through free functions
* -# \c checkPoint establishes, if the given state element represents a valid state
* -# \c checkPoint establishes if the given state element represents a valid state
* -# \c iterNext evolves this state by one step (sideeffect)
* -# \c yield realises the given state, yielding an element of result type T
*

View file

@ -50,14 +50,14 @@
** Thus an interface might accept a time::Control element \em reference (e.g. the
** lumiera::Play::Controller uses this pattern) -- meaning that the client owns the
** Control element and might attach listeners, while the implementation (server side)
** will attach the Control to mutate an time value entity otherwise not disclosed
** will attach the Control to mutate a time value entity otherwise not disclosed
** (e.g. the playhead position of the playback process). Of course, in this case
** the client is responsible for keeping the Control element and all listeners
** alive, and to invoke Control#disconnect prior to destroying the element.
**
** Of course, the reversed usage situation would be possible as well: an interface
** exposing a time::Control, thus allowing to attach target and listeners, while the
** actual changes will originate somewhere within the service implementation.
** may expose a time::Control, thus allowing to attach target and listeners, while the
** actual changes will originate somewhere within the opaque service implementation.
**
** Another usage pattern would be to expose a time::Control \c const&, allowing only to
** impose changes, but not to change the target or listener attachments. To the contrary,

View file

@ -104,9 +104,13 @@ namespace util {
/* === specialisations for STL containers and Lumiera Forward Iterators === */
/** operating on all elements of a STL container.
/** operate on all elements of a STL container.
* @note the container is taken by \c const& and
* the \c const is \em stripped before iteration.
* @todo reconsider if using rvalue references covers
* the "inline iteration" use case sufficiently,
* so that we can get rid of the unwrapping and
* thus get back to strict const correctness.
* @note this case is the default and kicks in
* \em unless we detect a Lumiera iterator.
* The handling is different for \c and_all
@ -126,7 +130,7 @@ namespace util {
}
/** operating on a Lumiera Forward Iterator until exhaustion. */
/** operate on a Lumiera Forward Iterator until exhaustion. */
template <typename IT
,typename FUN
>

View file

@ -6,6 +6,11 @@ TEST "Self-check: testing GUI backbone parts" TestGui_test <<END
return: 0
END
PLANNED "Concept demonstration: retrieve session contents" SessionStructureMapping_test <<END
return: 0
END
PLANNED "ModelClip_test" ModelClip_test <<END
END

View file

@ -1,5 +1,5 @@
/*
VisitingTool(Concept) - working out our own Visitor library implementation
VisitingTool(Concept) - concept draft of a Visitor library implementation
Copyright (C) Lumiera.org
2008, Hermann Vosseler <Ichthyostega@web.de>
@ -21,12 +21,12 @@
* *****************************************************/
/** @file visitingtoolconept.cpp
** While laying the foundations for Session and Builder, Ichthyo came across
/** @file visitingtool-conept.cpp
** While laying the foundations for Session and Builder, Ichthyo came across
** the necessity to create a custom implementation of the Visitor Pattern
** optimally suited for Lumiera's needs. This implementation file was
** used for the draft and is self-contained. The final solution was then
** extracted as library implementation to visitor.hpp
** optimally suited for Lumiera's needs. This implementation file was used
** for the drafting process and is self-contained. The final solution was
** then extracted later as library implementation into visitor.hpp
**
** Basic considerations
** <ul><li>cyclic dependencies should be avoided or at least restricted
@ -34,18 +34,18 @@
** user code should be as small as possible.</li>
** <li>Visitor is about <i>double dispatch</i>, thus we can't avoid
** using some table lookup implementation, and we can't avoid using
** some of the cooperating classes vtables. Besides that, the
** some of the cooperating classes' vtables. Besides that, the
** implementation should not be too wasteful...</li>
** <li>individual Visiting Tool implementation classes should be able
** to opt in or opt out on implementing functions treating some of
** the visitable subclasses.</li>
** <li>there should be a safe fallback mechanism backed by the
** visitable object's hierarchy relations. If some new class declares
** to be visitable, existing Visiting Tools not treating this new
** visitable type should fall back to the next best match in the
** hierarchy, not to some deaf base class</li>
** to be visitable, existing Visiting Tools not yet treating this new
** visitable type should fall back rather to the next best match up the
** hierarchy, instead of invoking some almost abstract base class</li>
** </ul>
**
**
** @see visitor.hpp the final lib implementation
** @see visitingtooltest.cpp test cases using our lib implementation
** @see BuilderTool one especially important instantiation
@ -71,7 +71,7 @@ namespace lumiera {
// ================================================================== Library ====
template<class TOOL> class Tag;
@ -111,10 +111,10 @@ namespace lumiera {
/** storage for the Tag registry for each concrete tool */
template<class TOOL, class TOOLImpl>
Tag<TOOL> TagTypeRegistry<TOOL,TOOLImpl>::tag;
template<class TOOL>
size_t Tag<TOOL>::lastRegisteredID (0);
@ -128,25 +128,27 @@ namespace lumiera {
typedef RET ReturnType;
typedef Tool<RET> ToolBase; ///< for templating the Tag and Dispatcher
virtual ~Tool () { }; ///< use RTTI for all visiting tools
virtual ~Tool() { }; ///< use RTTI for all visiting tools
/** allows discovery of the concrete Tool type when dispatching a
* visitor call. Can be implemented by inheriting from ToolType */
virtual Tag<ToolBase> getTag() = 0;
virtual Tag<ToolBase> getTag() =0;
};
/** Mixin for attaching a type tag to the concrete tool implementation */
template<class TOOLImpl, class BASE=Tool<void> >
class ToolType : public BASE
template<class TOOLImpl, class BASE =Tool<void>>
class ToolType
: public BASE
{
typedef typename BASE::ToolBase ToolBase;
public:
virtual Tag<ToolBase> getTag()
virtual Tag<ToolBase>
getTag()
{
TOOLImpl* typeref = 0;
return Tag<ToolBase>::get (typeref);
TOOLImpl* typeKey = 0;
return Tag<ToolBase>::get (typeKey);
}
};
@ -154,9 +156,9 @@ namespace lumiera {
/**
* For each posible call entry point via some subclass of the visitable hierarchy,
* For each possible call entry point via some subclass of the visitable hierarchy,
* we maintain a dispatcher table to keep track of all concrete tool implementations
* able to recieve and process calls on objects of this subclass.
* able to receive and process calls on objects of this subclass.
*/
template<class TAR, class TOOL>
class Dispatcher
@ -164,7 +166,7 @@ namespace lumiera {
typedef typename TOOL::ReturnType ReturnType;
/** generator for Trampoline functions,
* used to dispatch calls down to the
* used to dispatch calls down to the
* right "treat"-Function on the correct
* concrete tool implementation class
*/
@ -173,7 +175,7 @@ namespace lumiera {
callTrampoline (TAR& obj, TOOL& tool)
{
// cast down to real implementation type
CHECK (INSTANCEOF (TOOLImpl, &tool));
CHECK (INSTANCEOF (TOOLImpl, &tool));
TOOLImpl& toolObj = static_cast<TOOLImpl&> (tool);
// trigger (compile time) overload resolution
@ -183,19 +185,19 @@ namespace lumiera {
}
typedef ReturnType (*Trampoline) (TAR&, TOOL& );
/** VTable for storing the Trampoline pointers */
/** custom VTable for storing the Trampoline pointers */
std::vector<Trampoline> table_;
inline bool
is_known (size_t id)
{
return id<=table_.size() && table_[id-1];
return id<=table_.size() && table_[id-1];
}
inline void
inline void
storePtr (size_t id, Trampoline func)
{
// lacks error- and concurrency handling....
@ -204,7 +206,7 @@ namespace lumiera {
table_[id-1] = func;
}
inline Trampoline
inline Trampoline
storedTrampoline (size_t id)
{
if (id<=table_.size() && table_[id-1])
@ -218,12 +220,12 @@ namespace lumiera {
{
cout << "Error Handler: unregistered combination of (Tool, TargetObject) invoked!\n";
}
public:
static lib::Depend<Dispatcher<TAR,TOOL> > instance;
static lib::Depend<Dispatcher<TAR,TOOL>> instance;
inline ReturnType
inline ReturnType
forwardCall (TAR& target, TOOL& tool)
{
// get concrete type via tool's VTable
@ -232,10 +234,10 @@ namespace lumiera {
}
template<class TOOLImpl>
inline void
enrol(TOOLImpl* typeref)
inline void
enrol (TOOLImpl* typeKey)
{
Tag<TOOL>& index = Tag<TOOL>::get (typeref);
Tag<TOOL>& index = Tag<TOOL>::get (typeKey);
if (is_known (index))
return;
else
@ -246,18 +248,18 @@ namespace lumiera {
}
};
/** storage for the dispatcher table(s) */
template<class TAR, class TOOL>
lib::Depend<Dispatcher<TAR,TOOL> > Dispatcher<TAR,TOOL>::instance;
/**
* concrete visiting tool implementation has to inherit from this
* class for each kind of calls it wants to get dispatched,
* any concrete visiting tool implementation has to inherit from
* this class for each kind of calls it wants to get dispatched,
* Allowing us to record the type information.
*/
template<class TAR, class TOOLImpl, class BASE=Tool<void> >
@ -267,10 +269,10 @@ namespace lumiera {
typedef typename BASE::ToolBase ToolBase;
protected:
Applicable ()
Applicable()
{
TOOLImpl* typeref = 0;
Dispatcher<TAR,ToolBase>::instance().enrol (typeref);
TOOLImpl* typeKey = 0;
Dispatcher<TAR,ToolBase>::instance().enrol (typeKey);
}
virtual ~Applicable () {}
@ -289,19 +291,19 @@ namespace lumiera {
/** Marker interface "visitable object".
*/
template
< class TOOL = Tool<void>
< class TOOL = Tool<void>
>
class Visitable
{
protected:
virtual ~Visitable () { };
virtual ~Visitable() { };
/// @note may differ from TOOL
typedef typename TOOL::ToolBase ToolBase;
typedef typename TOOL::ReturnType ReturnType;
/** @internal used by the DEFINE_PROCESSABLE_BY macro.
* Dispatches to the actual operation on the
* Dispatches to the actual operation on the
* "visiting tool" (visitor implementation)
* Note: creates a context templated on concrete TAR.
*/
@ -329,16 +331,15 @@ namespace lumiera {
// =============================================================(End) Library ====
namespace test
{
namespace test {
typedef Tool<void> VisitingTool;
class HomoSapiens : public Visitable<>
{
public:
@ -356,7 +357,7 @@ namespace lumiera {
public:
DEFINE_PROCESSABLE_BY (VisitingTool);
};
class Leader : public Boss
{
};
@ -364,7 +365,7 @@ namespace lumiera {
class Visionary : public Leader
{
};
class VerboseVisitor
: public VisitingTool
@ -375,8 +376,8 @@ namespace lumiera {
cout << format ("Hello %s, nice to meet you...\n") % guy;
}
};
class Babbler
: public Applicable<Boss,Babbler>,
public Applicable<BigBoss,Babbler>,
@ -388,10 +389,10 @@ namespace lumiera {
void treat (BigBoss&) { talk_to("Big Boss"); }
};
@ -408,11 +409,11 @@ namespace lumiera {
*/
class VisitingTool_concept : public Test
{
virtual void run(Arg)
virtual void run(Arg)
{
known_visitor_known_class();
visitor_not_visiting_some_class();
}
}
void known_visitor_known_class()
{
@ -453,8 +454,4 @@ namespace lumiera {
} // namespace test
} // namespace visitor_concept_draft
} // namespace lumiera
}}} // namespace lumiera::visitor_concept_draft::test

View file

@ -100,6 +100,7 @@ namespace test {
void
retrieveSessionStructure ()
{
}
};

View file

@ -1,5 +1,5 @@
/*
UtilForeach(Test) - helpers for doing something for each element
UtilForeach(Test) - helpers to perform something for each element
Copyright (C) Lumiera.org
2009, Hermann Vosseler <Ichthyostega@web.de>

View file

@ -0,0 +1,171 @@
/*
VerbFunctionDispatch(Test) - Concept to dispatch according to the verbs of a DSL
Copyright (C) Lumiera.org
2014, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
#include "lib/test/run.hpp"
//#include "lib/util.hpp"
#include "lib/format-string.hpp"
//#include <boost/lexical_cast.hpp>
//#include <iostream>
#include <string>
//#include <map>
//using boost::lexical_cast;
//using util::contains;
using std::string;
using util::_Fmt;
//using std::cout;
//using std::endl;
namespace lib {
namespace test{
namespace {
const string BEGINNING("silence");
}
/**
* a receiver of verb-tokens,
* which renders them verbosely
*/
class VerboseRenderer
: public Receiver
{
string woof() { return "Woof-Woof!"; }
string honk() { return "Honk-Honk!"; }
string moo() { return "Moo-Moo!"; }
string meh() { return "Meh!"; }
};
/**
* Statefull receiver of verb-tokens.
*/
class RecollectingReceiver
: public Receiver
{
string verb_;
_Fmt fmt_;
string
buildResultTerm (string nextToken)
{
string resultExpression (fmt_ % verb_ % nextToken);
verb_ = nextToken;
return resultExpression;
}
string woof() { return buildResultTerm (WOOF); }
string honk() { return buildResultTerm (HONK); }
string moo() { return buildResultTerm (MOO); }
string meh() { return buildResultTerm (MEH); }
public:
RecollectingReceiver()
: verb_(BEGINNING)
, fmt_("%s followed by %s")
{ }
};
/***********************************************************************//**
* @test Demonstration/Concept: dispatch a specific function
* based on the given verbs of an embedded custom language.
* - weakness of
*
* @see HashIndexed_test
* @see
*/
class VerbFunctionDispatch_test : public Test
{
virtual void
run (Arg)
{
VerbSeq tokens = build_test_feed();
render_verbose (tokens);
verify_dispatch (tokens);
}
/** prepare a sequence of verbs
* for the actual tests to work on */
VerbSeq
build_test_feed()
{
return {
WOOF,
HONK,
MOO,
MEH
};
}
/** @test demonstrate the dispatching
* based on the concrete verb token.
* Here the implementation just prints
* the name of the invoked verb
*/
void
render_verbose (VerbSeq tokens)
{
VerboseRenderer receiver;
for (Verb verb : tokens)
cout << "consuming " << string(verb)
<< " -> '"
<< verb.applyTo(receiver)
<< "'\n";
}
/** @test verify the correct individual dispatch
* through a computation specific for the given verb
*/
void
verify_dispatch (VerbSeq tokens)
{
RecollectingReceiver receiver;
string previous = BEGINNING;
for (Verb verb : tokens)
{
CHECK (previous+" followed by "+string(verb) == verb.applyTo(receiver));
previous = string(verb);
}
}
};
/** Register this test class... */
LAUNCHER (VerbFunctionDispatch_test, "unit common");
}} // namespace lib::test

View file

@ -7911,32 +7911,32 @@ Thus we'll employ a special kind of media asset, which actually links to a [[bin
{{red{WIP as of 11/2010 -- most details yet to be defined}}}
</pre>
</div>
<div title="VisitingToolImpl" modifier="Ichthyostega" created="200801032003" modified="200802031822" tags="impl excludeMissing">
<pre>The ''Visitor Pattern'' is a special form of //double dispatch// &amp;mdash; selecting the function actually to be executed at runtime based both on the concrete type of some tool object //and // the target this tool is applied to. The rationale is to separate some specific implementation details from the basic infrastructure and the global interfaces, which can be limited to describe the fundamental properties and operations, while all details relevant only for some specific sub-problem can be kept together encapsulated in a tool implementation class. Typically, there is some iteration mechanism, allowing to apply these tools to all objects in a given container, a collection or object graph, without knowing the exact type of the target and tool objects. See the [[Visitor Pattern design discussion|VisitorUse]]
<div title="VisitingToolImpl" modifier="Ichthyostega" created="200801032003" modified="201411092355" tags="impl excludeMissing" changecount="11">
<pre>The ''Visitor Pattern'' is a special form of //double dispatch// &amp;mdash; the goal is to select the function actually to be executed at runtime based both on the concrete type of some tool object //and// the target this tool is applied to. The rationale is to separate some specific implementation details from the basic infrastructure and the global interfaces, which can be limited to describe the fundamental properties and operations, while all details relevant only for some specific sub-problem can be kept together encapsulated in a tool implementation class. Typically, there is some iteration mechanism, allowing to apply these tools to all objects in a given container, a collection or object graph, without knowing the exact type of the target and tool objects. See the [[Visitor Pattern design discussion|VisitorUse]]
!Problems with Visitor
The visitor pattern is not very popular, because any implementation is tricky, difficult to understand and often puts quite some burden on the user code. Even Erich Gamma says that on his list of bottom-ten patterns, Visitor is at the very bottom. This may be due to the fact that this original visitor implementation (often called the ''~GoF visitor'') causes a cyclic dependency between the target objects and the visiting tool objects, and includes some repetitive code (which results in silent failure if forgotten). Robert Martin invented 1996 an interesting variation (commonly labled ''acyclic visitor''). By using a marker base class for each concrete target type to be treated by the visitor, and by applying a dynamic cast, we can get rid of the cyclic dependencies. The top level &quot;Visitor&quot; is reduced to a mere empty marker interface in this design, while the actual visiting capabilities for a concrete target object is discovered at application time by the aforementioned dynamic cast. Besides the runntime cost of such a cast, the catch is that now the user code has still more responsibilities, because the need to maintain consistently two parallel object hierarchies.
Long time there seemed to be not much room for improvement, at least before the advent of generic programming and the discovery of template metaprogramming. ''Loki'' (Alexandrescu, 2000) showed us how to write a library implementation which hides away the technicallities of the visitor pattern and automatically generates most of the repetitive code.
The visitor pattern is not very popular, because any implementation is tricky, difficult to understand and tends to place quite some burden on the user code. Even Erich Gamma says that on his list of bottom-ten patterns, Visitor is at the very bottom. This may be due to the fact that this original visitor implementation (often called the ''~GoF visitor'') causes a cyclic dependency between the target objects and the visiting tool objects, and includes some repetitive code (which results in silent failure if forgotten). Robert Martin invented 1996 an interesting variation (commonly labelled ''acyclic visitor''). By using a marker base class for each concrete target type to be treated by the visitor, and by applying a dynamic cast, we can get rid of the cyclic dependencies. The top level &quot;Visitor&quot; is reduced to a mere empty marker interface in this design, while the actual visiting capabilities for a concrete target object is discovered at application time by the aforementioned dynamic cast. Besides the runtime cost of such a cast, the catch is that now the user code has still more responsibilities, because the need to maintain consistently two parallel object hierarchies.
Long time there seemed to be not much room for improvement, at least before the advent of generic programming and the discovery of template metaprogramming. ''Loki'' (Alexandrescu, 2000) showed us how to write a library implementation which hides away the technicalities of the visitor pattern and automatically generates most of the repetitive code.
!Requirements
* cyclic dependencies should be avoided or at least restricted to some central, library related place.
* The responsibilities for user code should be as small as possible. Especially, we should minimize the necessity to do corresponding adjustments to separate code locations, e.g. the necessity to maintain parallel hierarchies.
* Visitor is about //double dispatch,// thus we can't avoid using some table lookup implementation &amp;mdash; more specifically we can't avoid using the cooperating classes vtables. We can expect at least two table lookups for each call dispatch. Besides that, the implementation should not be too wasteful...
* individual &quot;visiting tool&quot; implementation classes should be able to opt in or opt out on implementing functions treating some of the visitable subclasses.
* individual &quot;visiting tool&quot; implementation classes should be able to opt in or opt out on implementing functions to treat some of the visitable subclasses.
* there should be a safe fallback mechanism backed by the visitable object's hierarchy relations. If some concrete visiting tool class decides not to implement a {{{treat(..)}}}-function for some concrete target type, the call should fall back to the next best match according to the target object's hierarchy, i.e. the next best {{{treat(..)}}}-function should be used.
The last requirement practically rules out the Loki acyclic visitor, because this implementation calls a general fallback function when an exact match based on the target object's type is not possible. Considering our use of the visitor pattern within the render engine builder, such a solution would not be of much use: Some specific builder tool may implement a {{{treat(CompoundClip&amp;)}}}-function, while most of the other builder tools just implement a {{{treat(Clip&amp;)}}}-function, thus handling any multichannel clip via the general clip interface. This is exactly the reason why we want to use visitor at first place. Isolate specific treatment, implement against interfaces.
The last requirement practically rules out the Loki acyclic visitor, because this implementation calls a general fallback function when an exact match based on the target object's type is not possible. Considering our use of the visitor pattern within the render engine builder, such a solution would not be of much use: Some specific builder tool may implement a {{{treat(CompoundClip&amp;)}}}-function, while most of the other builder tools just implement a {{{treat(Clip&amp;)}}}-function, thus handling any multichannel clip via the general clip interface. This is exactly the reason why we want to use visitor at first place. Reduce the scope of specific treatment, implement against rather generic interfaces.
!Implementation Notes
A good starting point for understanding our library implementation of the visitor pattern is {{{tests/components/common/visitingtoolconcept.cpp}}}, which contains a all-in-one-file proof of concept implementation, on which the real implementation ({{{&quot;common/visitor.hpp&quot;}}}) was based.
A good starting point to understand our library implementation of the visitor pattern is {{{tests/components/common/visitingtoolconcept.cpp}}}, which contains an complete, all-in-one-file proof of concept implementation, on which the real implementation ({{{&quot;common/visitor.hpp&quot;}}}) was based.
* similar to Loki, we use a {{{Visitable}}} base class and a {{{Tool}}} base class (we prefer the name &quot;Tool&quot; over &quot;Visitor&quot;, because it makes the intended use more clear).
* the objects in the {{{Visitable}}} hierarchy implement an {{{apply(Tool&amp;)}}}-function. This function needs to be implemented in a very specific manner, thus the {{{DEFINE_PROCESSABLE_BY}}} macro should be used when possible to insert the definition into a concrete {{{Visitable}}} class.
* similar to the acyclic visitor, the concrete visiting tool classes inherit from {{{Applicable&lt;TARGET, ...&gt;}}} marker base classes, where the template parameter {{{TARGET}}} is the concrete Visitable type this tool wants to treat, either directly by defining a {{{treat(ConcreteVisitable&amp;)}}}, or by falling back to some more general {{{treat(...)}}} function.
* consequently our implementation is //rather not acyclic// &amp;mdash; the concrete tool implementation depends on the full definition (header) of all concrete Visitables, but we avoid cyclic dependencies on the interface level. By using a typelist technique inspired by Loki, we can concentrate these dependencies in one single header file, which keeps things maintainable.
* we use the {{{Applicable&lt;TARGET, ...&gt;}}} marker base classes to drive the generation of Dispatcher classes, each of which holds a table of trampoline functions for carrying out the actual double dispatch at call time. Each concrete Visitable using the {{{DEFINE_PROCESSABLE_BY}}}-macro generates a separate Dispatcher table containing slots for each concrete tool implementation class. To store and access the index position for these &quot;call slots&quot;, we use a tag associated with the concrete visiting tool class, which can be retrieved by going though the tool's vtable
* we use the {{{Applicable&lt;TARGET, ...&gt;}}} marker base classes to drive the generation of Dispatcher classes, each of which holds a table of trampoline functions to carry out the actual double dispatch at call time. Each concrete Visitable, by using the {{{DEFINE_PROCESSABLE_BY}}}-macro, causes the generation of a dedicated, separate Dispatcher table, holding call slots for each concrete tool implementation class. To store and access the index position for these &quot;call slots&quot;, we use a tag associated with the concrete visiting tool class, which can be retrieved by going though the tool's vtable
* __runtime cost__: the concrete tool's ctor stores the trampoline pointers (this could be optimized to be a &quot;once per class&quot; initialisation). Then, for each call, we have 2 virtual function calls and a lookup and call of the trampoline function, typically resulting in another virtual function call for resolving the {{{treat(..)}}} function on the concrete tool class
* __extension possibilities__: while this system may seem complicated, you should note that it was designed with special focus on extension and implementing against interfaces:
** not every Visitable subclass needs a separate Dispatcher. As a rule of thumb, only when some type needs to be treated separately within some concrete visiting tool (i.e. when there is the need of a {{{treat(MySpecialVisitable&amp;)}}}), then this class should use the {{{DEFINE_PROCESSABLE_BY}}}-macro and thus define it's own {{{apply()}}}-function and Dispatcher. In all other cases, it is sufficient just to extend some existing Visitable, which thus will act as an interface with regards to the visiting tools.
** because the possibility of utilizing virtual {{{treat(...)}}} functions, not every concrete visiting tool class needs to define a set of {{{Applicable&lt;...&gt;}}} base classes (and thus get a separate dispatcher slot). We need such only for each //unique set// of Applicables. All other concrete tools can extend existing tool implementations, sharing and partially extending the same set of virtual {{{treat()}}}-functions.
* __extension possibilities__: while this system might seem overengineered, you should note the specific focus on extension and implementing against interfaces:
** not every Visitable subclass requires to build a separate Dispatcher. As a rule of thumb, only when a class needs dedicated and specific treatment within some concrete visiting tool (i.e. when there is the need of a function {{{treat(MySpecialVisitable&amp;)}}}), then this class should use the {{{DEFINE_PROCESSABLE_BY}}}-macro, leading to the definition of a distinct {{{apply()}}}-function and Dispatcher. In all other cases, it is sufficient just to extend some existing Visitable, which thus acts as an interface as far as visiting tools are concerned.
** because of the possibility of utilising virtual {{{treat(...)}}} functions, not every concrete visiting tool class needs to define a set of {{{Applicable&lt;...&gt;}}} base classes (and thus get a separate dispatcher slot). We need such only for each //unique set// of Applicables. All other concrete tools can extend existing tool implementations, sharing and partially extending the same set of virtual {{{treat()}}}-functions.
** when adding a new &quot;first class&quot; Visitable, i.e. a concrete target class that needs to be treated separately in some visiting tool, the user needs to include the {{{DEFINE_PROCESSABLE_BY}}} macro and needs to make sure that all existing &quot;first class&quot; tool implementation classes include the Applicable base class for this new type. In this respect, our implementation is clearly &quot;cyclic&quot;. (Generally speaking, the visitor pattern should not be used when the hierarchy of target objects is frequently extended and remoulded). But, when using the typelist facillity to define the Applicable base classes, we'll have one header file defining these collection of Applicables and thus we just need to add our new concrete Visitable to this header and recompile all tool implementation classes.
** when creating a new &quot;~Visitable-and-Tool&quot; hierarchy, the user should derive (or typedef) and parametrize the {{{Visitable}}}, {{{Tool}}} and {{{Applicable}}} templates, typically into a new namespace. An example can be seen in {{{proc/mobject/builder/buildertool.hpp}}}
</pre>