diff --git a/src/common/visitor.hpp b/src/common/visitor.hpp index b3323bc8a..c9a375f92 100644 --- a/src/common/visitor.hpp +++ b/src/common/visitor.hpp @@ -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 + template + < typename RET = void, + class TOOL = Tool, + template 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 - static RET dispatchOp(TAR& target, Tool& tool) + static RET dispatchOp(TAR& target, TOOL& tool) { if (Applicable* concreteTool = dynamic_cast*> (&tool)) @@ -100,7 +108,7 @@ namespace cinelerra return concreteTool->treat (target); else - return ReturnType(); + return ERR::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); } diff --git a/src/common/visitorpolicies.hpp b/src/common/visitorpolicies.hpp new file mode 100644 index 000000000..c4baa1646 --- /dev/null +++ b/src/common/visitorpolicies.hpp @@ -0,0 +1,92 @@ +/* + VISITOR.hpp - Acyclic Visitor library + + Copyright (C) CinelerraCV + 2007, 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 + +*/ + + + +#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 + struct UseDefault + { + template + static RET onUnknown (TAR&, TOOL&) + { + return RET(); + } + }; + + /** + * Policy to throw when encountering an unknown visiting tool + */ + template + struct ThrowException + { + template + 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 + struct InvokeCatchAllFunction + { + template + static RET onUnknown (TAR& target,TOOL& tool) + { + target.apply (static_cast (tool)); + } + }; + + + } // namespace visitor + +} // namespace cinelerra +#endif diff --git a/src/proc/mobject/buildable.hpp b/src/proc/mobject/buildable.hpp index 4e8d4df6c..fd65bd1f7 100644 --- a/src/proc/mobject/buildable.hpp +++ b/src/proc/mobject/buildable.hpp @@ -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&); }; diff --git a/tests/50components.tests b/tests/50components.tests index 10de4c7b4..6c6c5f20c 100644 --- a/tests/50components.tests +++ b/tests/50components.tests @@ -182,7 +182,10 @@ out: --> remaining=SingleTestID spam --eggs END -PLANNED "VisitingTool_test" VisitingTool_test < +#include #include -//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, + public Applicable + { + 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