/* VISITOR.hpp - Generic Visitor library implementation Copyright (C) Lumiera.org 2008, Hermann Vosseler 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 Proc 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 ** ** For design questions and more detailed implementation notes, see the Proc Layer 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 ERR = UseDefault > class Tool : public ERR { 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 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 : public BASE { } ; template < class TOOLImpl, class TAR, class TYPES, class BASE > class Applicable, BASE> : public Applicable { typedef typename BASE::ToolBase ToolBase; protected: virtual ~Applicable () {} Applicable () { TOOLImpl* typeref = 0; Dispatcher::instance().enrol (typeref); } public: virtual Tag getTag () { TOOLImpl* typeref = 0; return Tag::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 static inline ReturnType dispatchOp (TAR& target, TOOL& tool) { return Dispatcher::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