introcuded a catch-all hook when applying visiting tools to a hierarchy

This commit is contained in:
Fischlurch 2007-11-28 04:19:21 +01:00
parent 4e99e8b66c
commit 78a4bf8e6b
5 changed files with 176 additions and 20 deletions

View file

@ -34,6 +34,7 @@ This code is heavily inspired by
#ifndef CINELERRA_VISITOR_H
#define CINELERRA_VISITOR_H
#include "common/visitorpolicies.hpp"
namespace cinelerra
@ -66,22 +67,29 @@ namespace cinelerra
class Applicable
{
public:
typedef RET ReturnType;
virtual ~Applicable () { };
/** to be implemented by concrete tools
* wanting to visit type TAR */
virtual RET treat (TAR& visitable) = 0;
};
/** Marker interface / base class for all types
* to be treated by a "visiting tool" or visitor.
*/
template <typename RET = void>
template
< typename RET = void,
class TOOL = Tool,
template<typename,class> class ERR = UseDefault
>
class Visitable
{
public:
/** to be defined by the DEFINE_PROCESSABLE_BY macro
* in all classes wanting to be treated by some tool */
virtual RET apply (TOOL&) = 0;
typedef RET ReturnType;
protected:
@ -92,7 +100,7 @@ namespace cinelerra
* "visiting tool" (acyclic visitor implementation)
*/
template <class TAR>
static RET dispatchOp(TAR& target, Tool& tool)
static RET dispatchOp(TAR& target, TOOL& tool)
{
if (Applicable<TAR,RET>* concreteTool
= dynamic_cast<Applicable<TAR,RET>*> (&tool))
@ -100,7 +108,7 @@ namespace cinelerra
return concreteTool->treat (target);
else
return ReturnType();
return ERR<RET,TOOL>::onUnknown (target,tool);
}
};
@ -109,8 +117,8 @@ namespace cinelerra
* by some "visiting tool". Defines the apply-function,
* which is the actual access point to invoke the visiting
*/
#define DEFINE_VISITABLE() \
virtual ReturnType apply (Tool& tool) \
#define DEFINE_PROCESSABLE_BY(TOOL) \
virtual ReturnType apply (TOOL& tool) \
{ return dispatchOp (*this, tool); }

View file

@ -0,0 +1,92 @@
/*
VISITOR.hpp - Acyclic Visitor library
Copyright (C) CinelerraCV
2007, 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
*/
#ifndef CINELERRA_VISITORPOLICIES_H
#define CINELERRA_VISITORPOLICIES_H
#include "common/error.hpp"
namespace cinelerra
{
namespace visitor
{
/* == several Policies usable in conjunction with cinelerra::visitor::Visitable == */
/**
* Policy returning just the default return value in case
* of encountering an unknown Visitor (typically caused by
* adding a new class to the visitable hierarchy)
*/
template<typename RET, class TOOL>
struct UseDefault
{
template<class TAR>
static RET onUnknown (TAR&, TOOL&)
{
return RET();
}
};
/**
* Policy to throw when encountering an unknown visiting tool
*/
template<typename RET, class TOOL>
struct ThrowException
{
template<class TAR>
static RET onUnknown (TAR&, TOOL&)
{
throw cinelerra::error::Config("unable to decide what tool operation to call");
}
};
/**
* Policy invoking an catch-all function for processing
* an unknown tool / target pair
*/
template<typename RET, class TOOL>
struct InvokeCatchAllFunction
{
template<class TAR>
static RET onUnknown (TAR& target,TOOL& tool)
{
target.apply (static_cast<TOOL&> (tool));
}
};
} // namespace visitor
} // namespace cinelerra
#endif

View file

@ -36,6 +36,7 @@ namespace mobject
namespace builder{ class BuilderTool; }
using cinelerra::visitor::Visitable;
using cinelerra::visitor::InvokeCatchAllFunction;
/**
@ -43,7 +44,11 @@ namespace mobject
* The actual operation is thus selected at runtime based both on the
* actual type of the Tool class /and/ the actual type of the Buildabele.
*/
class Buildable : public Visitable<>
class Buildable : public Visitable
< void, // return type of apply
builder::BuilderTool, // visiting tool base class
InvokeCatchAllFunction // how to handle unknown
>
{
public:
/** Catch-all implementation for applying any builder tool
@ -52,7 +57,7 @@ namespace mobject
* different Buildable subclasses specifically and the concrete Buildables
* will define explicitly to be specifically visitable.
*/
virtual ReturnType apply (builder::BuilderTool&);
virtual void apply (builder::BuilderTool&);
};

View file

@ -182,7 +182,10 @@ out: --> remaining=SingleTestID spam --eggs
END
PLANNED "VisitingTool_test" VisitingTool_test <<END
TEST "VisitingTool_test" VisitingTool_test <<END
out: Hello Boss, nice to meet you...
out: Hello big Boss, nice to meet you...
return: 0
END

View file

@ -26,22 +26,60 @@
//#include "common/factory.hpp"
//#include "common/util.hpp"
//#include <boost/format.hpp>
#include <boost/format.hpp>
#include <iostream>
//using boost::format;
using boost::format;
using std::string;
using std::cout;
namespace mobject
namespace cinelerra
{
namespace builder
namespace visitor
{
namespace test
{
class HomoSapiens : public Visitable<>
{
public:
DEFINE_PROCESSABLE_BY (Tool);
};
class Boss : public HomoSapiens
{
public:
DEFINE_PROCESSABLE_BY (Tool);
};
class BigBoss : public Boss
{
public:
DEFINE_PROCESSABLE_BY (Tool);
};
class Leader : public Boss
{
};
class VerboseVisitor
: public Tool,
public Applicable<Boss>,
public Applicable<BigBoss>
{
void talk_to (string guy)
{
cout << format ("Hello %s, nice to meet you...\n") % guy;
}
public:
void treat (Boss&) { talk_to("Boss"); }
void treat (BigBoss&) { talk_to("big Boss"); }
};
/*************************************************************************
@ -58,15 +96,25 @@ namespace mobject
{
virtual void run(Arg arg)
{
UNIMPLEMENTED ("testing the generic visitor pattern");
known_visitor_known_class();
visitor_not_visiting_some_class();
visitor_treating_new_subclass();
TODO ("implement the more complicated visitor test cases");
//visitor_not_visiting_some_class();
//visitor_treating_new_subclass();
}
void known_visitor_known_class()
{
UNIMPLEMENTED ("testing the generic visitor pattern");
HomoSapiens x1;
Boss x2;
BigBoss x3;
HomoSapiens& xx2 (x2);
HomoSapiens& xx3 (x3);
VerboseVisitor wizzy;
x1.apply (wizzy);
xx2.apply (wizzy);
xx3.apply (wizzy);
}
void visitor_not_visiting_some_class()
@ -88,6 +136,6 @@ namespace mobject
} // namespace test
} // namespace builder
} // namespace visitor
} // namespace mobject
} // namespace cinelerra