LUMIERA.clone/src/lib/visitor.hpp
Ichthyostega 20f3252892 Upgrade: down with typename!!
Yet another chainsaw massacre.

One of the most obnoxious annoyances with C++ metaprogramming
is the need to insert `typename` and `template` qualifiers into
most definitions, to help the compiler to cope with the syntax,
which is not context-free.

The recent standards adds several clarifications, so that most
of these qualifiers are redundant now, at least at places where
it is unambiguously clear that only a type can be given.

GCC already supports most of these relaxing rules
(Clang unfortunately lags way behind with support of newer language features...)
2025-07-06 01:19:08 +02:00

229 lines
8 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
VISITOR.hpp - Generic Visitor library implementation
Copyright (C)
2008, Hermann Vosseler <Ichthyostega@web.de>
  **Lumiera** 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. See the file COPYING for further details.
====================================================================
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:
using ReturnType = RET; ///< Tool function invocation return type
using ToolBase = Tool; ///< 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::Nil, 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>
{
using ToolBase = BASE::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:
using ReturnType = TOOL::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
using ToolBase = TOOL::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