2007-12-22 08:45:09 +01:00
|
|
|
|
/*
|
2014-11-10 04:00:39 +01:00
|
|
|
|
VisitingTool(Concept) - concept draft of a Visitor library implementation
|
2010-12-17 23:28:49 +01:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
Copyright (C)
|
|
|
|
|
|
2008, Hermann Vosseler <Ichthyostega@web.de>
|
2010-12-17 23:28:49 +01:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
**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.
|
2010-12-17 23:28:49 +01:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
* *****************************************************************/
|
2007-12-22 08:45:09 +01:00
|
|
|
|
|
|
|
|
|
|
|
2017-04-02 04:22:51 +02:00
|
|
|
|
/** @file visitingtool-concept.cpp
|
2014-11-10 04:00:39 +01:00
|
|
|
|
** While laying the foundations for Session and Builder, Ichthyo came across
|
2007-12-22 08:45:09 +01:00
|
|
|
|
** the necessity to create a custom implementation of the Visitor Pattern
|
2014-11-10 04:00:39 +01:00
|
|
|
|
** 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
|
2007-12-22 08:45:09 +01:00
|
|
|
|
**
|
2025-04-26 23:59:29 +02:00
|
|
|
|
** \par Basic considerations
|
|
|
|
|
|
** - cyclic dependencies should be avoided or at least restricted
|
|
|
|
|
|
** to some library related place. The responsibilities for
|
|
|
|
|
|
** user code should be as small as possible.
|
|
|
|
|
|
** - the purpose of Visitor is to achieve **double dispatch**, thus we
|
|
|
|
|
|
** can not avoid using some table lookup implementation, and we can not
|
|
|
|
|
|
** avoid using some of the cooperating classes' vtables. Besides that,
|
|
|
|
|
|
** the implementation should not be too wasteful...
|
|
|
|
|
|
** - individual Visiting Tool implementation classes should be able
|
|
|
|
|
|
** to opt in or opt out on implementing functions treating some of
|
|
|
|
|
|
** the visitable subclasses.
|
|
|
|
|
|
** - 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 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.
|
2014-11-10 04:00:39 +01:00
|
|
|
|
**
|
2007-12-22 08:45:09 +01:00
|
|
|
|
** @see visitor.hpp the final lib implementation
|
|
|
|
|
|
** @see visitingtooltest.cpp test cases using our lib implementation
|
2010-02-22 03:52:52 +01:00
|
|
|
|
** @see BuilderTool one especially important instantiation
|
2007-12-22 08:45:09 +01:00
|
|
|
|
**
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
2008-12-18 04:47:41 +01:00
|
|
|
|
#include "lib/test/run.hpp"
|
2016-01-07 03:58:29 +01:00
|
|
|
|
#include "lib/format-cout.hpp"
|
|
|
|
|
|
#include "lib/format-string.hpp"
|
2013-10-20 03:19:36 +02:00
|
|
|
|
#include "lib/depend.hpp"
|
2007-12-22 08:45:09 +01:00
|
|
|
|
|
2007-12-24 06:12:29 +01:00
|
|
|
|
#include <vector>
|
2007-12-22 08:45:09 +01:00
|
|
|
|
|
2016-01-07 03:58:29 +01:00
|
|
|
|
using util::_Fmt;
|
2007-12-22 08:45:09 +01:00
|
|
|
|
using std::string;
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-10-20 03:19:36 +02:00
|
|
|
|
namespace lumiera {
|
|
|
|
|
|
namespace visitor_concept_draft {
|
|
|
|
|
|
|
2007-12-22 08:45:09 +01:00
|
|
|
|
// ================================================================== Library ====
|
2008-01-04 01:44:40 +01:00
|
|
|
|
|
|
|
|
|
|
|
2014-11-10 04:00:39 +01:00
|
|
|
|
|
2008-01-04 01:44:40 +01:00
|
|
|
|
template<class TOOL> class Tag;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<class TOOL, class TOOLImpl>
|
|
|
|
|
|
struct TagTypeRegistry
|
|
|
|
|
|
{
|
|
|
|
|
|
static Tag<TOOL> tag;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
2007-12-24 06:12:29 +01:00
|
|
|
|
template<class TOOL>
|
|
|
|
|
|
class Tag
|
|
|
|
|
|
{
|
|
|
|
|
|
size_t tagID;
|
|
|
|
|
|
static size_t lastRegisteredID;
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
2008-01-04 01:44:40 +01:00
|
|
|
|
Tag() : tagID(0) { }
|
2007-12-24 06:12:29 +01:00
|
|
|
|
operator size_t() const { return tagID; }
|
|
|
|
|
|
|
2008-01-04 01:44:40 +01:00
|
|
|
|
|
2007-12-24 06:12:29 +01:00
|
|
|
|
template<class TOOLImpl>
|
2008-01-04 01:44:40 +01:00
|
|
|
|
static Tag&
|
2009-01-10 19:03:28 +01:00
|
|
|
|
get (TOOLImpl* const =0) // param used to pass type info
|
2007-12-24 06:12:29 +01:00
|
|
|
|
{
|
2008-01-04 15:10:18 +01:00
|
|
|
|
// we have a race condition here...
|
2008-01-04 01:44:40 +01:00
|
|
|
|
Tag& t = TagTypeRegistry<TOOL,TOOLImpl>::tag;
|
2007-12-24 06:12:29 +01:00
|
|
|
|
if (!t)
|
2008-01-04 01:44:40 +01:00
|
|
|
|
t.tagID = ++lastRegisteredID;
|
2007-12-24 06:12:29 +01:00
|
|
|
|
return t;
|
|
|
|
|
|
}
|
2008-01-04 01:44:40 +01:00
|
|
|
|
|
2007-12-24 06:12:29 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
2008-01-04 01:44:40 +01:00
|
|
|
|
|
2007-12-24 06:12:29 +01:00
|
|
|
|
/** storage for the Tag registry for each concrete tool */
|
|
|
|
|
|
template<class TOOL, class TOOLImpl>
|
2008-01-04 01:44:40 +01:00
|
|
|
|
Tag<TOOL> TagTypeRegistry<TOOL,TOOLImpl>::tag;
|
2014-11-10 04:00:39 +01:00
|
|
|
|
|
2007-12-24 06:12:29 +01:00
|
|
|
|
template<class TOOL>
|
|
|
|
|
|
size_t Tag<TOOL>::lastRegisteredID (0);
|
2014-11-10 04:00:39 +01:00
|
|
|
|
|
2007-12-24 06:12:29 +01:00
|
|
|
|
|
2008-01-04 01:44:40 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Marker interface "visiting tool" */
|
2007-12-22 08:45:09 +01:00
|
|
|
|
template<typename RET>
|
|
|
|
|
|
class Tool
|
|
|
|
|
|
{
|
|
|
|
|
|
public:
|
2025-07-05 20:08:18 +02:00
|
|
|
|
using ReturnType = RET;
|
|
|
|
|
|
using ToolBase = Tool<RET>; ///< for templating the Tag and Dispatcher
|
2007-12-24 06:12:29 +01:00
|
|
|
|
|
2014-11-10 04:00:39 +01:00
|
|
|
|
virtual ~Tool() { }; ///< use RTTI for all visiting tools
|
2007-12-22 08:45:09 +01:00
|
|
|
|
|
2008-01-04 01:44:40 +01:00
|
|
|
|
/** allows discovery of the concrete Tool type when dispatching a
|
|
|
|
|
|
* visitor call. Can be implemented by inheriting from ToolType */
|
2014-11-10 04:00:39 +01:00
|
|
|
|
virtual Tag<ToolBase> getTag() =0;
|
2008-01-04 01:44:40 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Mixin for attaching a type tag to the concrete tool implementation */
|
2014-11-10 04:00:39 +01:00
|
|
|
|
template<class TOOLImpl, class BASE =Tool<void>>
|
|
|
|
|
|
class ToolType
|
|
|
|
|
|
: public BASE
|
2008-01-04 01:44:40 +01:00
|
|
|
|
{
|
2025-07-05 20:08:18 +02:00
|
|
|
|
using ToolBase = BASE::ToolBase;
|
2008-01-04 01:44:40 +01:00
|
|
|
|
|
|
|
|
|
|
public:
|
2014-11-10 04:00:39 +01:00
|
|
|
|
virtual Tag<ToolBase>
|
|
|
|
|
|
getTag()
|
2008-01-04 01:44:40 +01:00
|
|
|
|
{
|
2014-11-10 04:00:39 +01:00
|
|
|
|
TOOLImpl* typeKey = 0;
|
|
|
|
|
|
return Tag<ToolBase>::get (typeKey);
|
2007-12-24 06:12:29 +01:00
|
|
|
|
}
|
2007-12-22 08:45:09 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
2007-12-24 06:12:29 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2014-11-10 04:00:39 +01:00
|
|
|
|
* For each possible call entry point via some subclass of the visitable hierarchy,
|
2007-12-24 06:12:29 +01:00
|
|
|
|
* we maintain a dispatcher table to keep track of all concrete tool implementations
|
2014-11-10 04:00:39 +01:00
|
|
|
|
* able to receive and process calls on objects of this subclass.
|
2007-12-24 06:12:29 +01:00
|
|
|
|
*/
|
|
|
|
|
|
template<class TAR, class TOOL>
|
|
|
|
|
|
class Dispatcher
|
2007-12-22 08:45:09 +01:00
|
|
|
|
{
|
2025-07-05 20:08:18 +02:00
|
|
|
|
using ReturnType = TOOL::ReturnType;
|
2007-12-22 08:45:09 +01:00
|
|
|
|
|
2007-12-24 06:12:29 +01:00
|
|
|
|
/** generator for Trampoline functions,
|
2014-11-10 04:00:39 +01:00
|
|
|
|
* used to dispatch calls down to the
|
2007-12-24 06:12:29 +01:00
|
|
|
|
* right "treat"-Function on the correct
|
|
|
|
|
|
* concrete tool implementation class
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<class TOOLImpl>
|
2008-01-04 01:44:40 +01:00
|
|
|
|
static ReturnType
|
|
|
|
|
|
callTrampoline (TAR& obj, TOOL& tool)
|
2007-12-22 08:45:09 +01:00
|
|
|
|
{
|
2007-12-24 06:12:29 +01:00
|
|
|
|
// cast down to real implementation type
|
2014-11-10 04:00:39 +01:00
|
|
|
|
CHECK (INSTANCEOF (TOOLImpl, &tool));
|
2007-12-24 06:12:29 +01:00
|
|
|
|
TOOLImpl& toolObj = static_cast<TOOLImpl&> (tool);
|
|
|
|
|
|
|
|
|
|
|
|
// trigger (compile time) overload resolution
|
2008-01-04 01:44:40 +01:00
|
|
|
|
// based on concrete type, then dispatch the call.
|
2007-12-24 06:12:29 +01:00
|
|
|
|
// Note this may cause obj to be upcasted.
|
|
|
|
|
|
return toolObj.treat (obj);
|
2007-12-22 08:45:09 +01:00
|
|
|
|
}
|
2007-12-24 06:12:29 +01:00
|
|
|
|
|
|
|
|
|
|
typedef ReturnType (*Trampoline) (TAR&, TOOL& );
|
|
|
|
|
|
|
2014-11-10 04:00:39 +01:00
|
|
|
|
|
|
|
|
|
|
/** custom VTable for storing the Trampoline pointers */
|
2007-12-24 06:12:29 +01:00
|
|
|
|
std::vector<Trampoline> table_;
|
2008-01-04 01:44:40 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inline bool
|
|
|
|
|
|
is_known (size_t id)
|
|
|
|
|
|
{
|
2025-06-07 23:59:57 +02:00
|
|
|
|
return id<=table_.size() and table_[id-1];
|
2008-01-04 01:44:40 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-11-10 04:00:39 +01:00
|
|
|
|
inline void
|
2008-01-04 01:44:40 +01:00
|
|
|
|
storePtr (size_t id, Trampoline func)
|
|
|
|
|
|
{
|
2008-01-04 15:10:18 +01:00
|
|
|
|
// lacks error- and concurrency handling....
|
2008-01-04 01:44:40 +01:00
|
|
|
|
if (id>table_.size())
|
2008-01-04 15:10:18 +01:00
|
|
|
|
table_.resize (id);
|
2008-01-04 01:44:40 +01:00
|
|
|
|
table_[id-1] = func;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-11-10 04:00:39 +01:00
|
|
|
|
inline Trampoline
|
2008-01-04 01:44:40 +01:00
|
|
|
|
storedTrampoline (size_t id)
|
|
|
|
|
|
{
|
2025-06-07 23:59:57 +02:00
|
|
|
|
if (id<=table_.size() and table_[id-1])
|
2008-01-04 01:44:40 +01:00
|
|
|
|
return table_[id-1];
|
|
|
|
|
|
else
|
|
|
|
|
|
return &errorHandler;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static ReturnType
|
|
|
|
|
|
errorHandler (TAR&, TOOL&)
|
|
|
|
|
|
{
|
2008-01-04 15:10:18 +01:00
|
|
|
|
cout << "Error Handler: unregistered combination of (Tool, TargetObject) invoked!\n";
|
2008-01-04 01:44:40 +01:00
|
|
|
|
}
|
2014-11-10 04:00:39 +01:00
|
|
|
|
|
2008-01-04 01:44:40 +01:00
|
|
|
|
|
2007-12-24 06:12:29 +01:00
|
|
|
|
public:
|
2014-11-10 04:00:39 +01:00
|
|
|
|
static lib::Depend<Dispatcher<TAR,TOOL>> instance;
|
2008-01-04 01:44:40 +01:00
|
|
|
|
|
2014-11-10 04:00:39 +01:00
|
|
|
|
inline ReturnType
|
2007-12-24 06:12:29 +01:00
|
|
|
|
forwardCall (TAR& target, TOOL& tool)
|
|
|
|
|
|
{
|
2008-01-05 12:16:07 +01:00
|
|
|
|
// get concrete type via tool's VTable
|
2007-12-24 06:12:29 +01:00
|
|
|
|
Tag<TOOL> index = tool.getTag();
|
2008-01-04 01:44:40 +01:00
|
|
|
|
return (*storedTrampoline(index)) (target, tool);
|
2007-12-24 06:12:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<class TOOLImpl>
|
2014-11-10 04:00:39 +01:00
|
|
|
|
inline void
|
|
|
|
|
|
enrol (TOOLImpl* typeKey)
|
2007-12-22 08:45:09 +01:00
|
|
|
|
{
|
2014-11-10 04:00:39 +01:00
|
|
|
|
Tag<TOOL>& index = Tag<TOOL>::get (typeKey);
|
2008-01-04 01:44:40 +01:00
|
|
|
|
if (is_known (index))
|
|
|
|
|
|
return;
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Trampoline func = &callTrampoline<TOOLImpl>;
|
|
|
|
|
|
storePtr (index, func);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-12-22 08:45:09 +01:00
|
|
|
|
}
|
|
|
|
|
|
};
|
2014-11-10 04:00:39 +01:00
|
|
|
|
|
2008-01-04 01:44:40 +01:00
|
|
|
|
/** storage for the dispatcher table(s) */
|
|
|
|
|
|
template<class TAR, class TOOL>
|
2013-10-20 03:19:36 +02:00
|
|
|
|
lib::Depend<Dispatcher<TAR,TOOL> > Dispatcher<TAR,TOOL>::instance;
|
2014-11-10 04:00:39 +01:00
|
|
|
|
|
2008-01-04 01:44:40 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2007-12-24 06:12:29 +01:00
|
|
|
|
/**
|
2014-11-10 04:00:39 +01:00
|
|
|
|
* any concrete visiting tool implementation has to inherit from
|
|
|
|
|
|
* this class for each kind of calls it wants to get dispatched,
|
2007-12-24 06:12:29 +01:00
|
|
|
|
* Allowing us to record the type information.
|
|
|
|
|
|
*/
|
2008-01-04 01:44:40 +01:00
|
|
|
|
template<class TAR, class TOOLImpl, class BASE=Tool<void> >
|
2007-12-24 06:12:29 +01:00
|
|
|
|
class Applicable
|
2007-12-22 08:45:09 +01:00
|
|
|
|
{
|
2025-07-05 20:08:18 +02:00
|
|
|
|
using Ret = BASE::ReturnType;
|
|
|
|
|
|
using ToolBase = BASE::ToolBase;
|
2007-12-24 06:12:29 +01:00
|
|
|
|
|
2008-01-04 01:44:40 +01:00
|
|
|
|
protected:
|
2014-11-10 04:00:39 +01:00
|
|
|
|
Applicable()
|
2007-12-22 08:45:09 +01:00
|
|
|
|
{
|
2014-11-10 04:00:39 +01:00
|
|
|
|
TOOLImpl* typeKey = 0;
|
|
|
|
|
|
Dispatcher<TAR,ToolBase>::instance().enrol (typeKey);
|
2007-12-22 08:45:09 +01:00
|
|
|
|
}
|
2007-12-24 06:12:29 +01:00
|
|
|
|
|
2008-01-04 01:44:40 +01:00
|
|
|
|
virtual ~Applicable () {}
|
|
|
|
|
|
|
2007-12-24 06:12:29 +01:00
|
|
|
|
public:
|
2008-01-04 01:44:40 +01:00
|
|
|
|
// we could enforce the definition of treat()-functions by:
|
|
|
|
|
|
//
|
|
|
|
|
|
// virtual Ret treat (TAR& target) = 0;
|
2007-12-24 06:12:29 +01:00
|
|
|
|
|
2007-12-22 08:45:09 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
2007-12-24 06:12:29 +01:00
|
|
|
|
|
2008-01-04 15:10:18 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2007-12-22 08:45:09 +01:00
|
|
|
|
/** Marker interface "visitable object".
|
|
|
|
|
|
*/
|
|
|
|
|
|
template
|
2014-11-10 04:00:39 +01:00
|
|
|
|
< class TOOL = Tool<void>
|
2007-12-22 08:45:09 +01:00
|
|
|
|
>
|
|
|
|
|
|
class Visitable
|
|
|
|
|
|
{
|
|
|
|
|
|
protected:
|
2014-11-10 04:00:39 +01:00
|
|
|
|
virtual ~Visitable() { };
|
2007-12-22 08:45:09 +01:00
|
|
|
|
|
2008-01-04 01:44:40 +01:00
|
|
|
|
/// @note may differ from TOOL
|
2025-07-05 20:08:18 +02:00
|
|
|
|
using ToolBase = TOOL::ToolBase;
|
|
|
|
|
|
using ReturnType = TOOL::ReturnType;
|
2008-01-04 01:44:40 +01:00
|
|
|
|
|
|
|
|
|
|
/** @internal used by the DEFINE_PROCESSABLE_BY macro.
|
2014-11-10 04:00:39 +01:00
|
|
|
|
* Dispatches to the actual operation on the
|
2008-01-04 01:44:40 +01:00
|
|
|
|
* "visiting tool" (visitor implementation)
|
|
|
|
|
|
* Note: creates a context templated on concrete TAR.
|
2007-12-22 08:45:09 +01:00
|
|
|
|
*/
|
|
|
|
|
|
template <class TAR>
|
2008-01-04 01:44:40 +01:00
|
|
|
|
static inline ReturnType
|
|
|
|
|
|
dispatchOp (TAR& target, TOOL& tool)
|
2007-12-22 08:45:09 +01:00
|
|
|
|
{
|
2008-01-04 01:44:40 +01:00
|
|
|
|
return Dispatcher<TAR,ToolBase>::instance().forwardCall (target,tool);
|
2007-12-22 08:45:09 +01:00
|
|
|
|
}
|
2008-01-04 01:44:40 +01:00
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
/** to be defined by the DEFINE_PROCESSABLE_BY macro
|
|
|
|
|
|
* in all classes wanting to be treated by some tool */
|
|
|
|
|
|
virtual ReturnType apply (TOOL&) = 0;
|
2007-12-22 08:45:09 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
2008-01-04 01:44:40 +01:00
|
|
|
|
|
2007-12-22 08:45:09 +01:00
|
|
|
|
/** mark a Visitable subclass as actually treatable
|
|
|
|
|
|
* by some "visiting tool". 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); }
|
|
|
|
|
|
|
|
|
|
|
|
|
2008-01-04 01:44:40 +01:00
|
|
|
|
// =============================================================(End) Library ====
|
2007-12-24 06:12:29 +01:00
|
|
|
|
|
|
|
|
|
|
|
2007-12-22 08:45:09 +01:00
|
|
|
|
|
2008-01-04 01:44:40 +01:00
|
|
|
|
|
2014-11-10 04:00:39 +01:00
|
|
|
|
|
|
|
|
|
|
namespace test {
|
|
|
|
|
|
|
2025-07-05 20:08:18 +02:00
|
|
|
|
using VisitingTool = Tool<void>;
|
2014-11-10 04:00:39 +01:00
|
|
|
|
|
2007-12-22 08:45:09 +01:00
|
|
|
|
class HomoSapiens : public Visitable<>
|
|
|
|
|
|
{
|
|
|
|
|
|
public:
|
2008-01-04 01:44:40 +01:00
|
|
|
|
DEFINE_PROCESSABLE_BY (VisitingTool);
|
2007-12-22 08:45:09 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class Boss : public HomoSapiens
|
|
|
|
|
|
{
|
|
|
|
|
|
public:
|
2008-01-04 01:44:40 +01:00
|
|
|
|
DEFINE_PROCESSABLE_BY (VisitingTool);
|
2007-12-22 08:45:09 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class BigBoss : public Boss
|
|
|
|
|
|
{
|
|
|
|
|
|
public:
|
2008-01-04 01:44:40 +01:00
|
|
|
|
DEFINE_PROCESSABLE_BY (VisitingTool);
|
2007-12-22 08:45:09 +01:00
|
|
|
|
};
|
2014-11-10 04:00:39 +01:00
|
|
|
|
|
2007-12-22 08:45:09 +01:00
|
|
|
|
class Leader : public Boss
|
|
|
|
|
|
{
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class Visionary : public Leader
|
|
|
|
|
|
{
|
|
|
|
|
|
};
|
2014-11-10 04:00:39 +01:00
|
|
|
|
|
2008-01-04 01:44:40 +01:00
|
|
|
|
|
2007-12-22 08:45:09 +01:00
|
|
|
|
class VerboseVisitor
|
2008-01-04 01:44:40 +01:00
|
|
|
|
: public VisitingTool
|
2007-12-22 08:45:09 +01:00
|
|
|
|
{
|
|
|
|
|
|
protected:
|
|
|
|
|
|
void talk_to (string guy)
|
|
|
|
|
|
{
|
2016-01-07 03:58:29 +01:00
|
|
|
|
cout << _Fmt{"Hello %s, nice to meet you...\n"} % guy;
|
2007-12-22 08:45:09 +01:00
|
|
|
|
}
|
|
|
|
|
|
};
|
2014-11-10 04:00:39 +01:00
|
|
|
|
|
|
|
|
|
|
|
2007-12-22 08:45:09 +01:00
|
|
|
|
class Babbler
|
2008-01-04 01:44:40 +01:00
|
|
|
|
: public Applicable<Boss,Babbler>,
|
|
|
|
|
|
public Applicable<BigBoss,Babbler>,
|
|
|
|
|
|
public Applicable<Visionary,Babbler>,
|
|
|
|
|
|
public ToolType<Babbler, VerboseVisitor>
|
2007-12-22 08:45:09 +01:00
|
|
|
|
{
|
|
|
|
|
|
public:
|
|
|
|
|
|
void treat (Boss&) { talk_to("Boss"); }
|
2008-01-04 01:44:40 +01:00
|
|
|
|
void treat (BigBoss&) { talk_to("Big Boss"); }
|
|
|
|
|
|
|
2007-12-22 08:45:09 +01:00
|
|
|
|
};
|
2014-11-10 04:00:39 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2007-12-22 08:45:09 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-10-24 23:06:36 +02:00
|
|
|
|
/*********************************************************************//**
|
2007-12-22 08:45:09 +01:00
|
|
|
|
* @test build and run some common cases for developing and verifying
|
|
|
|
|
|
* ichthyo's implementation concept for the Visitor Pattern.
|
|
|
|
|
|
* Defines a hierarchy of test classes to check the following cases
|
|
|
|
|
|
* <ul><li>calling the correct visiting tool specialized function
|
|
|
|
|
|
* for given concrete hierarchy classes</li>
|
|
|
|
|
|
* <li>visiting tool not declaring to visit some class</li>
|
2008-01-04 15:10:18 +01:00
|
|
|
|
* <li>newly added and not properly declared Visitable class
|
|
|
|
|
|
* causes the dispatcher to invoke an error handler</li>
|
2007-12-22 08:45:09 +01:00
|
|
|
|
* </ul>
|
|
|
|
|
|
*/
|
|
|
|
|
|
class VisitingTool_concept : public Test
|
|
|
|
|
|
{
|
2014-11-10 04:00:39 +01:00
|
|
|
|
virtual void run(Arg)
|
2007-12-22 08:45:09 +01:00
|
|
|
|
{
|
|
|
|
|
|
known_visitor_known_class();
|
|
|
|
|
|
visitor_not_visiting_some_class();
|
2014-11-10 04:00:39 +01:00
|
|
|
|
}
|
2007-12-22 08:45:09 +01:00
|
|
|
|
|
|
|
|
|
|
void known_visitor_known_class()
|
|
|
|
|
|
{
|
|
|
|
|
|
Boss x1;
|
|
|
|
|
|
BigBoss x2;
|
|
|
|
|
|
|
|
|
|
|
|
// masquerade as HomoSapiens...
|
|
|
|
|
|
HomoSapiens& homo1 (x1);
|
|
|
|
|
|
HomoSapiens& homo2 (x2);
|
|
|
|
|
|
|
|
|
|
|
|
cout << "=== Babbler meets Boss and BigBoss ===\n";
|
|
|
|
|
|
Babbler bab;
|
2008-01-04 01:44:40 +01:00
|
|
|
|
VisitingTool& vista (bab);
|
2007-12-22 08:45:09 +01:00
|
|
|
|
homo1.apply (vista);
|
|
|
|
|
|
homo2.apply (vista);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void visitor_not_visiting_some_class()
|
|
|
|
|
|
{
|
|
|
|
|
|
HomoSapiens x1;
|
|
|
|
|
|
Visionary x2;
|
|
|
|
|
|
|
|
|
|
|
|
HomoSapiens& homo1 (x1);
|
|
|
|
|
|
HomoSapiens& homo2 (x2);
|
|
|
|
|
|
|
|
|
|
|
|
cout << "=== Babbler meets HomoSapiens and Visionary ===\n";
|
|
|
|
|
|
Babbler bab;
|
2008-01-04 01:44:40 +01:00
|
|
|
|
VisitingTool& vista (bab);
|
|
|
|
|
|
homo1.apply (vista); // error handler (not Applicable to HomoSapiens)
|
2007-12-22 08:45:09 +01:00
|
|
|
|
homo2.apply (vista); // treats Visionary as Boss
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Register this test class... */
|
|
|
|
|
|
LAUNCHER (VisitingTool_concept, "unit common");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-11-10 04:00:39 +01:00
|
|
|
|
}}} // namespace lumiera::visitor_concept_draft::test
|