LUMIERA.clone/src/lib/visitor.hpp

238 lines
8.5 KiB
C++

/*
VISITOR.hpp - Generic Visitor library implementation
Copyright (C) Lumiera.org
2008, 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.
====================================================================
This code is heavily inspired by
The Loki Library (loki-lib/trunk/include/loki/Visitor.h)
Copyright (c) 2001 by Andrei Alexandrescu
This Loki code accompanies the book:
Alexandrescu, Andrei. "Modern C++ Design: Generic Programming
and Design Patterns Applied".
Copyright (c) 2001. Addison-Wesley. ISBN 0201704315
Credits for many further implementation ideas go to
Cooperative Visitor: A Template Technique for Visitor Creation
by Anand Shankar Krishnamoorthi (July 2007)
http://www.artima.com/cppsource/cooperative_visitor.html
*/
/** @file visitor.hpp
** A library implementation of the *Visitor Pattern* tailored specifically
** to Lumiera's needs within the Steam Layer. Visitor enables *double dispatch*
** calls, based both on the concrete type of some target object and the concrete type of
** a tool object being applied to this target. The code carrying out this tool application
** (and thus triggering the double dispatch) need not know any of these concrete types and is
** thus completely decoupled form implementation details encapsulated within the visiting tool.
** The visiting tool implementation class provides specific "treat(ConcreteVisitable&)" functions,
** and this visitor lib will dispatch the call to the* correct "treat"-function based on the
** concrete target visitable type.
**
** Implementation notes
** - driven by dispatch tables with trampoline functions.
** - uses Typelists and Template metaprogramming to generate
** Dispatcher tables for the concrete types.
** - individual Visiting Tool implementation classes need to derive
** from some Applicable<TOOLImpl, Types<Type1,Type2,...> > instantiation
** and thus define which calls they get dispatched. _This is 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.
** - 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 `apply(TOOLBASE&)` function. Otherwise, it will be treated by the
** interface of the next base class using this macro.
**
** @remarks as of 2016, it is not clear if we'll really use this facility; it was meant to play a
** crucial role in the Builder (which is not implemented yet....). The fundamental idea of relying
** on a visitor seems still adequate though. For design questions and more detailed implementation
** notes, see the [TiddlyWiki].
**
** [TiddlyWiki]: http://lumiera.org/wiki/renderengine.html#VisitorUse%20VisitingToolImpl%20BuilderStructures%20BuilderMechanics "Lumiera Tiddly Wiki"
**
** @see visitingtooltest.cpp test cases using our lib implementation
** @see BuilderTool one especially important instantiation
** @see visitor-dispatcher.hpp
** @see typelist.hpp
**
*/
#ifndef LIB_VISITOR_H
#define LIB_VISITOR_H
#include "lib/visitor-policies.hpp"
#include "lib/visitor-dispatcher.hpp"
#include "lib/meta/typelist.hpp"
namespace lib {
namespace visitor {
namespace typelist = lib::meta;
/**
* Marker interface / base class for all "visiting tools".
* When applying such a tool to some concrete instance
* derived from Visitable, a special function treating
* this concrete subclass will be selected on the
* concrete visiting tool instance.
*/
template
< typename RET = void
, template <class> class ERR = UseDefault
>
class Tool : public ERR<RET>
{
public:
typedef RET ReturnType; ///< Tool function invocation return type
typedef Tool ToolBase; ///< for templating the Tag and Dispatcher
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 ToolTag */
virtual Tag<ToolBase> getTag() = 0;
};
/**
* Marker template to declare that some "visiting tool"
* wants to treat a set of concrete Visitable classes.
*
* Each "first class" concrete visiting tool implementation has
* to inherit from an instance of this template parametrised 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 pre-register the
* dispatcher entry.
*/
template
< class TOOLImpl, // concrete tool implementation type
class TYPES, // List of applicable Types goes here...
class BASE=Tool<> // "visiting tool" base class
>
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::ToolBase ToolBase;
protected:
virtual ~Applicable () {}
Applicable ()
{
TOOLImpl* typeref = 0;
Dispatcher<TAR,ToolBase>::instance().enrol (typeref);
}
public:
virtual Tag<ToolBase>
getTag ()
{
TOOLImpl* typeref = 0;
return Tag<ToolBase>::get (typeref);
}
};
using typelist::Types; // convenience for the user of "Applicable"
/**
* Marker interface or base class for all "Visitables".
* Concrete types to be treated by a "visiting tool" derive from
* this interface and need to implement an #apply(Tool&), forwarding
* to the (internal, static, templated) #dispatchOp. This is done
* best by using the #DEFINE_PROCESSABLE_BY macro.
*/
template
< class TOOL = Tool<>
>
class Visitable
{
public:
typedef typename TOOL::ReturnType ReturnType;
/** to be defined by the DEFINE_PROCESSABLE_BY macro
* in all classes wanting to be treated by some tool */
virtual ReturnType apply (TOOL&) = 0;
protected:
virtual ~Visitable () { };
/// @note may differ from TOOL
typedef typename TOOL::ToolBase ToolBase;
/** @internal used by the #DEFINE_PROCESSABLE_BY macro.
* Dispatches to the actual operation on the
* "visiting tool" (visitor implementation)
* Note: creates a context templated on concrete TAR.
*/
template <class TAR>
static inline ReturnType
dispatchOp (TAR& target, TOOL& tool)
{
return Dispatcher<TAR,ToolBase>::instance().forwardCall (target,tool);
}
};
/** mark a Visitable subclass as actually treat-able by some
* "visiting tool" base interface. Defines the apply-function,
* which is the actual access point to invoke the visiting
*/
#define DEFINE_PROCESSABLE_BY(TOOL) \
virtual ReturnType apply (TOOL& tool) \
{ return dispatchOp (*this, tool); }
}} // namespace lib::visitor
#endif