use typelists to declare to visit a collection of types
This commit is contained in:
parent
bf93301211
commit
8fe1a901ba
7 changed files with 83 additions and 64 deletions
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
====================================================================
|
||||
This code is heavily inspired by
|
||||
The Loki Library (loki-lib/trunk/include/loki/Singleton.h)
|
||||
The Loki Library (loki-lib/trunk/include/loki/Sequence.h)
|
||||
Copyright (c) 2001 by Andrei Alexandrescu
|
||||
Copyright (c) 2005 by Peter Kümmel
|
||||
This Loki code accompanies the book:
|
||||
|
|
|
|||
|
|
@ -29,8 +29,7 @@ This code is heavily inspired by
|
|||
|
||||
Credits for many further implementation ideas go to
|
||||
Cooperative Visitor: A Template Technique for Visitor Creation
|
||||
by Anand Shankar Krishnamoorthi
|
||||
July 11, 2007
|
||||
by Anand Shankar Krishnamoorthi (July 2007)
|
||||
http://www.artima.com/cppsource/cooperative_visitor.html
|
||||
|
||||
*/
|
||||
|
|
@ -52,10 +51,16 @@ Credits for many further implementation ideas go to
|
|||
** <li>uses Typelists and Template metaprogramming to generate
|
||||
** Dispatcher tables for the concrete types.</li>
|
||||
** <li>individual Visiting Tool implementation classes need to derive
|
||||
** from some Applicable<TARGET, TOOLImpl> instantiation and thus
|
||||
** define which calls they get dispatched. They are free to implement
|
||||
** corresponding "treat(ConcreteVisitable&) functions or fall back
|
||||
** from some Applicable<TOOLImpl, Types<Type1,Type2,...> > instantiation
|
||||
** and thus define which calls they get dispatched. This is \b crucial.
|
||||
** A concrete type not declared in this way will never be dispatched to this
|
||||
** concrete visiting tool implementation class. Of course, the latter is free
|
||||
** to implement corresponding "treat(ConcreteVisitable&) functions or fall back
|
||||
** on some treat(VisitableInterface&) function.</li>
|
||||
** <li>any concrete Visitable subclass wanting to be treated by some concrete tool
|
||||
** needs to use the DECLARE_PROCESSABLE_BY(TOOLBASE) macro. By this, it gets an
|
||||
** virtual \code apply(TOOLBASE&) function. Otherwise, it will be treated by the
|
||||
** interface of the next base clas using this macro.</li>
|
||||
** </ul>
|
||||
** For design questions and more detailed implementation notes, see the Proc Layer Tiddly Wiki.
|
||||
**
|
||||
|
|
@ -74,6 +79,8 @@ Credits for many further implementation ideas go to
|
|||
#include "common/visitorpolicies.hpp"
|
||||
#include "common/visitordispatcher.hpp"
|
||||
|
||||
#include "common/typelist.hpp"
|
||||
|
||||
|
||||
namespace cinelerra
|
||||
{
|
||||
|
|
@ -106,54 +113,70 @@ namespace cinelerra
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* Mixin for attaching a type tag to the concrete tool implementation
|
||||
*/
|
||||
template<class TOOLImpl, class BASE=Tool<> >
|
||||
class ToolTag : public BASE
|
||||
{
|
||||
typedef typename BASE::ToolBase ToolBase;
|
||||
|
||||
public:
|
||||
virtual Tag<ToolBase> getTag()
|
||||
{
|
||||
TOOLImpl* typeref = 0;
|
||||
return Tag<ToolBase>::get (typeref);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Mixin template to declare that some "visiting tool"
|
||||
* wants to treat a concrete subclass of Visitable.
|
||||
* Marker template to declare that some "visiting tool"
|
||||
* wants to treat a set of concrete Visitable classes.
|
||||
*
|
||||
* Concrete visiting tool implementation has to inherit from
|
||||
* an instance of this template class for each kind of calls
|
||||
* it wants to get dispatched, allowing us to record the type
|
||||
* information and register the dispatcher entry via the
|
||||
* automatic base ctor call.
|
||||
* Each "first class" concrete visiting tool implementation has
|
||||
* to inherit from an instance of this template parametrized with
|
||||
* the desired types; for each of the mentioned types, calls will
|
||||
* be dispatched to the tool implementation. (To make it clear:
|
||||
* Calls to all other types not marked by such an "Applicable"
|
||||
* won't ever be dispatched to this tool class.).
|
||||
* A Sideeffect of inheriting from such an "Applicable" is that
|
||||
* the tool gets an unique Tag entry, which is used internally
|
||||
* as index in the dispatcher tables. And the automatic ctor call
|
||||
* allows us to record the type information and preregister the
|
||||
* dispatcher entry.
|
||||
*/
|
||||
template
|
||||
< class TAR, // concrete Visitable to be treated
|
||||
class TOOLImpl, // concrete tool implementation type
|
||||
class BASE=Tool<> // "visiting tool" base class
|
||||
< class TOOLImpl, // concrete tool implementation type
|
||||
class TYPES, // List of applicable Types goes here...
|
||||
class BASE=Tool<> // "visiting tool" base class
|
||||
>
|
||||
class Applicable
|
||||
class Applicable;
|
||||
|
||||
template // recursion end: inherit from BASE
|
||||
< class TOOLImpl,
|
||||
class BASE
|
||||
>
|
||||
class Applicable<TOOLImpl, typelist::NullType, BASE>
|
||||
: public BASE
|
||||
{ }
|
||||
;
|
||||
|
||||
template
|
||||
< class TOOLImpl,
|
||||
class TAR, class TYPES,
|
||||
class BASE
|
||||
>
|
||||
class Applicable<TOOLImpl, typelist::Node<TAR, TYPES>, BASE>
|
||||
: public Applicable<TOOLImpl, TYPES, BASE>
|
||||
{
|
||||
typedef typename BASE::ReturnType Ret;
|
||||
|
||||
typedef typename BASE::ToolBase ToolBase;
|
||||
|
||||
protected:
|
||||
virtual ~Applicable () {}
|
||||
Applicable ()
|
||||
{
|
||||
TOOLImpl* typeref = 0;
|
||||
Dispatcher<TAR,ToolBase>::instance().enroll (typeref);
|
||||
}
|
||||
|
||||
virtual ~Applicable () {}
|
||||
public:
|
||||
virtual Tag<ToolBase>
|
||||
getTag ()
|
||||
{
|
||||
TOOLImpl* typeref = 0;
|
||||
return Tag<ToolBase>::get (typeref);
|
||||
}
|
||||
};
|
||||
|
||||
using typelist::Types; // convienience for the user of "Applicable"
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -56,28 +56,22 @@ namespace mobject
|
|||
* each Tool contains the concrete implementation for one task to be done
|
||||
* to the various MObject classes. The concrete builder tool implementation
|
||||
* should not diretcly inherit from this base interface, but rather through
|
||||
* an instantiation of the BuilderToolTag template. Additionally, it should
|
||||
* inherit from the Applicable template parametrized with all conctrete
|
||||
* Buildable classes, for which it wants calls to be dispatched.
|
||||
* an instantiation of the "Applicable" template parametrized with all
|
||||
* concrete Buildable classes, for which it wants calls to be dispatched.
|
||||
*/
|
||||
typedef cinelerra::visitor::Tool<void, InvokeCatchAllFunction> BuilderTool;
|
||||
|
||||
|
||||
template<class TOOLImpl>
|
||||
class BuilderToolTag
|
||||
: public cinelerra::visitor::ToolTag<TOOLImpl, BuilderTool>
|
||||
{ }
|
||||
;
|
||||
|
||||
|
||||
template
|
||||
< class TAR, // concrete Buildable to be treated
|
||||
class TOOLImpl // concrete BuilderTool implementation
|
||||
< class TOOLImpl, // concrete BuilderTool implementation
|
||||
class TYPELIST // list of all concrete Buildables to be treated
|
||||
>
|
||||
class Applicable
|
||||
: public cinelerra::visitor::Applicable<TAR,TOOLImpl, BuilderTool>
|
||||
: public cinelerra::visitor::Applicable<TOOLImpl, TYPELIST, BuilderTool>
|
||||
{ }
|
||||
;
|
||||
|
||||
using cinelerra::typelist::Types; // convienience for the users of "Applicable"
|
||||
|
||||
} // namespace mobject::builder
|
||||
|
||||
|
|
|
|||
|
|
@ -71,9 +71,10 @@ namespace cinelerra
|
|||
};
|
||||
|
||||
class Babbler
|
||||
: public Applicable<Boss,Babbler>,
|
||||
public Applicable<BigBoss,Babbler>,
|
||||
public ToolTag<Babbler, VerboseVisitor<Tool> >
|
||||
: public Applicable< Babbler,
|
||||
Types<Boss,BigBoss>::List, // treat this types
|
||||
VerboseVisitor<Tool> // intermediary base class
|
||||
>
|
||||
{
|
||||
public:
|
||||
void treat (Boss&) { talk_to("Boss"); }
|
||||
|
|
@ -119,8 +120,10 @@ namespace cinelerra
|
|||
* tailored for the Chief hierarchy
|
||||
*/
|
||||
class Blatherer
|
||||
: public Applicable<Visionary,Blatherer,Hastalavista>,
|
||||
public ToolTag<Blatherer, VerboseVisitor<Hastalavista> >
|
||||
: public Applicable< Blatherer,
|
||||
Types<Visionary>::List, // get calls to Visionary dispatched
|
||||
VerboseVisitor<Hastalavista> // note: different tool base class
|
||||
>
|
||||
{
|
||||
public:
|
||||
void treat (Leader&) { talk_to("Mr.Future"); }
|
||||
|
|
|
|||
|
|
@ -80,10 +80,10 @@ namespace cinelerra
|
|||
};
|
||||
|
||||
class Babbler
|
||||
: public Applicable<Boss,Babbler>,
|
||||
public Applicable<BigBoss,Babbler>,
|
||||
public Applicable<Visionary,Babbler>,
|
||||
public ToolTag<Babbler, VerboseVisitor>
|
||||
: public Applicable< Babbler
|
||||
, Types<Boss,BigBoss,Visionary>::List // dispatch calls to this types
|
||||
, VerboseVisitor
|
||||
>
|
||||
{
|
||||
public:
|
||||
void treat (Boss&) { talk_to("Boss"); }
|
||||
|
|
@ -155,7 +155,7 @@ namespace cinelerra
|
|||
|
||||
|
||||
|
||||
} // namespace test
|
||||
} // namespace test1
|
||||
|
||||
} // namespace visitor
|
||||
|
||||
|
|
|
|||
|
|
@ -51,9 +51,8 @@ namespace mobject
|
|||
DEFINE_PROCESSABLE_BY (BuilderTool);
|
||||
};
|
||||
|
||||
class TestTool : public BuilderToolTag<TestTool>,
|
||||
public Applicable<Clip,TestTool>,
|
||||
public Applicable<AbstractMO,TestTool>
|
||||
class TestTool
|
||||
: public Applicable<TestTool, Types<Clip,AbstractMO>::List>
|
||||
{
|
||||
public:
|
||||
void treat (Clip& c) { cout << "media is: "<< str(c.getMedia()) <<"\n"; }
|
||||
|
|
|
|||
|
|
@ -3971,7 +3971,7 @@ Using transitions is a very basic task and thus needs viable support by the GUI.
|
|||
Because of this experience, ichthyo wants to support a more general case of transitions, which have N output connections, behave similar to their "simple" counterpart, but leave out the mixing step. As a plus, such transitions can be inserted at the source ports of N clips or between any intermediary or final output ports as well. Any transition processor capable of handling this situation should provide some flag, in order to decide if he can be placed in such a manner. (wichin the builder, encountering a inconsistently placed transition is just an [[building error|BuildingError]])
|
||||
</pre>
|
||||
</div>
|
||||
<div title="VisitingToolImpl" modifier="Ichthyostega" modified="200801041046" created="200801032003" tags="impl" changecount="19">
|
||||
<div title="VisitingToolImpl" modifier="Ichthyostega" modified="200801051747" created="200801032003" tags="impl" changecount="20">
|
||||
<pre>The ''Visitor Pattern'' is a special form of //double dispatch// &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]]
|
||||
|
||||
!Problems with Visitor
|
||||
|
|
@ -3992,13 +3992,13 @@ A good starting point for understanding our library implementation of the visito
|
|||
* the objects in the {{{Visitable}}} hierarchy implement an {{{apply(Tool&)}}}-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<TARGET, ...>}}} 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&)}}}, or by falling back to some more general {{{treat(...)}}} function.
|
||||
* consequently our implementation is //rather not acyclic// &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<TARGET,TOOLImpl>}}} 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 "call slots", 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<TARGET, ...>}}} 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 "call slots", 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 "once per class" 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&)}}}), 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<...>}}} 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 "first class" 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 "first class" tool implementation classes include the Applicable base class for this new type. In this respect, our implementation is clearly "cyclic". (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 "~Visitable-and-Tool" hierarchy, the user should derive (or typedef) and parametrize the {{{Visitable}}}, {{{Tool}}} and {{{Applicable}}} templates, typically into a new namespace.
|
||||
** when creating a new "~Visitable-and-Tool" 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>
|
||||
</div>
|
||||
<div title="VisitorUse" modifier="Ichthyostega" modified="200801041025" created="200711280302" tags="impl discuss" changecount="16">
|
||||
|
|
|
|||
Loading…
Reference in a new issue