From 46b200809e0c65d12b465a187728e751bc321b86 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 22 Dec 2007 08:45:09 +0100 Subject: [PATCH] WIP desperately trying to work out a really usable visitor implementation --- doc/devel/Doxyfile | 5 +- src/common/visitorpolicies.hpp | 3 +- src/proc/mobject/buildable.cpp | 4 +- src/proc/mobject/buildable.hpp | 18 +- src/proc/mobject/builder/buildertool.hpp | 12 +- src/proc/mobject/placement.hpp | 4 + src/proc/mobject/session/abstractmo.hpp | 4 +- .../components/common/visitingtoolconcept.cpp | 296 ++++++++++++++++++ tests/components/common/visitingtooltest.cpp | 1 + .../proc/mobject/builder/buildertooltest.cpp | 4 +- 10 files changed, 341 insertions(+), 10 deletions(-) create mode 100644 tests/components/common/visitingtoolconcept.cpp diff --git a/doc/devel/Doxyfile b/doc/devel/Doxyfile index 21e8d51c1..7023172ca 100644 --- a/doc/devel/Doxyfile +++ b/doc/devel/Doxyfile @@ -25,7 +25,8 @@ ABBREVIATE_BRIEF = "The $name class" \ ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = YES -STRIP_FROM_PATH = ../../src/ +STRIP_FROM_PATH = ../../src/ \ + ../../tests/ STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES @@ -178,7 +179,7 @@ TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- -GENERATE_LATEX = YES +GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex diff --git a/src/common/visitorpolicies.hpp b/src/common/visitorpolicies.hpp index 2caadf5b2..a3b071cef 100644 --- a/src/common/visitorpolicies.hpp +++ b/src/common/visitorpolicies.hpp @@ -80,7 +80,8 @@ namespace cinelerra { static RET onUnknown (TAR& target,TOOL& tool) { - tool.treat (target); + //tool.treat (target); + tool.catchy (target); } }; diff --git a/src/proc/mobject/buildable.cpp b/src/proc/mobject/buildable.cpp index e37e9f7d0..de1e87cbd 100644 --- a/src/proc/mobject/buildable.cpp +++ b/src/proc/mobject/buildable.cpp @@ -32,12 +32,12 @@ namespace mobject * contain overloaded fuctions for treating * different Buildable subclasses specifically */ - Buildable::ReturnType +/* Buildable::ReturnType Buildable::apply (builder::BuilderTool& tool) { return dispatchOp (*this, tool); } - +*/ } // namespace mobject diff --git a/src/proc/mobject/buildable.hpp b/src/proc/mobject/buildable.hpp index fd65bd1f7..b7bbe5483 100644 --- a/src/proc/mobject/buildable.hpp +++ b/src/proc/mobject/buildable.hpp @@ -57,8 +57,24 @@ namespace mobject * different Buildable subclasses specifically and the concrete Buildables * will define explicitly to be specifically visitable. */ - virtual void apply (builder::BuilderTool&); +// virtual void apply (builder::BuilderTool&); ////////////////////////////// besser weg???? + virtual void fallback(builder::BuilderTool&) = 0; }; + +/** mark a Buildable subclass as actually treatable + * by some BuilderTool. Note this defines a more concrete + * apply-function, which actually dispatches with a + * Placement. Crutial to make the builder work. + */ +#define DEFINE_BUILDABLE \ + virtual void apply (builder::BuilderTool& tool) \ + { return dispatchOp (*this, tool); } + + ///////////////////////////////bringt das überhaupt was??? + +#define DEFINE_FALLBACK \ + virtual void fallback(builder::BuilderTool& tool) \ + { apply(tool); } diff --git a/src/proc/mobject/builder/buildertool.hpp b/src/proc/mobject/builder/buildertool.hpp index 330a4a8da..a4d6da5fc 100644 --- a/src/proc/mobject/builder/buildertool.hpp +++ b/src/proc/mobject/builder/buildertool.hpp @@ -41,7 +41,7 @@ namespace mobject * Used according to the visitor pattern: each Tool contains * the concrete implementation for one task to be done to the various MObject classes */ - class BuilderTool : public Tool + class BuilderTool : public Tool //////////////////////////auf die Zielklasse templaten und Placement festmachen??? { protected: typedef mobject::Buildable Buildable; @@ -49,8 +49,16 @@ namespace mobject public: /** This operation is to be overloaded for specific MObject subclasses to be treated. */ - virtual void treat (Buildable& mElement) = 0; + //virtual void treat (Buildable& mElement) = 0; + template + void catchy (BB& elem) {elem.fallback(*this); } }; + + template + void zoing(TO& ttt,BO& bot) + { + ttt->treat(bot); + } } // namespace mobject::builder diff --git a/src/proc/mobject/placement.hpp b/src/proc/mobject/placement.hpp index ebca6436d..cd2ee06ac 100644 --- a/src/proc/mobject/placement.hpp +++ b/src/proc/mobject/placement.hpp @@ -102,6 +102,9 @@ namespace mobject } virtual ~Placement() {}; + + /** */ /////////////////////////////////////////////////////////////TODO: totmachen? + // DEFINE_PROCESSABLE_BY (builder::BuilderTool); /** interface for defining the kind of placement @@ -150,6 +153,7 @@ namespace mobject (shared_ptr::operator-> ()); \ } \ }; + // DEFINE_PROCESSABLE_BY (builder::BuilderTool); /* a note to the maintainer: please don't add any fields or methods to * these subclasses which aren't also present in Placement! diff --git a/src/proc/mobject/session/abstractmo.hpp b/src/proc/mobject/session/abstractmo.hpp index bd4fd5785..ccb08cd83 100644 --- a/src/proc/mobject/session/abstractmo.hpp +++ b/src/proc/mobject/session/abstractmo.hpp @@ -47,7 +47,9 @@ namespace mobject virtual Time& getLength() { return length; } - DEFINE_PROCESSABLE_BY (builder::BuilderTool); +// DEFINE_PROCESSABLE_BY (builder::BuilderTool); + DEFINE_BUILDABLE; + DEFINE_FALLBACK; }; diff --git a/tests/components/common/visitingtoolconcept.cpp b/tests/components/common/visitingtoolconcept.cpp new file mode 100644 index 000000000..e5067740d --- /dev/null +++ b/tests/components/common/visitingtoolconcept.cpp @@ -0,0 +1,296 @@ +/* + VisitingTool(Concept) - working out our own Visitor library implementation + + 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. + +* *****************************************************/ + + +/** @file visitingtoolconept.cpp + ** While laying the foundations for EDL and Builder, Ichthyo came accross + ** the necessity to create a custom implementation of the Visitor Pattern + ** optimally suited for Cinelerra's needs. This implementation file was + ** used for the draft and is self-contained. The final solution was then + ** extracted as library implementation to visitor.hpp + ** + ** Basic considerations + **
  • cyclic dependencies should be avoided or at least restricted + ** to some library related place
  • + **
  • Visitor is about double dispatch, thus we can't avoid + ** using some table lookup implementation, and we can't avoid using + ** some of the cooperating classes vtables. Besides that, the + ** implementation should not be too wastefull
  • + **
  • individual Visiting Tool implementation classes should be able + ** to opt in or opt out on implementing function 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 treating this new + ** visitable type should fall back to the next best match in the + ** hierarchy, not to some deaf base class
  • + **
+ ** + ** @see visitor.hpp the final lib implementation + ** @see visitingtooltest.cpp test cases using our lib implementation + ** @see BuilderTool one especially important instantiiation + ** + */ + + +#include "common/test/run.hpp" +//#include "common/factory.hpp" +//#include "common/util.hpp" + +#include +#include + +using boost::format; +using std::string; +using std::cout; + + +namespace cinelerra + { + namespace visitor + { + // ================================================================== Library ==== + + /** Marker interface "visiting tool". + */ + template + class Tool + { + public: + typedef RET ReturnType; + + virtual ~Tool () { }; ///< use RTTI for all visiting tools + }; + + + template + class ToolRegistry + { + + }; + + + template + class ToolReg + { + //getTag() + void regCombination(Invoker ii) + { + ////OOOOhhh, da hab ich wiiiieder das gleiche Problem: wie krieg ich die Typinfo TAR???? + } + }; + + // alternativ ("halb-zyklisch") + // + template + class ReToo + { + ReToo() + { + //expliziten Eintrag machen für TAR und TOOLTYPE + } + }; + + template + class DisTab : ReToo, + ReToo + { + }; + + // der konkrete Dispatch instantiiert diese Klasse + // Voraussetzung: alle Tools hängen nur per Name von ihren Targets ab + + + template + class Dispatcher + { + // Ctor: for_each(ToolRegistry, call regCombination() ) + public: + static TOOL::ReturnType call (TOOL& tool, TAR& target) + { + + } + }; + + + /** Marker interface "visitable object". + */ + template + < class TOOL = Tool + > + class Visitable + { + public: + /** to be defined by the DEFINE_PROCESSABLE_BY macro + * in all classes wanting to be treated by some tool */ + typedef TOOL::ReturnType ReturnType; + virtual ReturnType apply (TOOL&) = 0; + + protected: + virtual ~Visitable () { }; + + /** @internal used by the DEFINE_VISITABLE macro. + * Dispatches to the actual operation on the + * "visiting tool" (acyclic visitor implementation) + */ + template + static ReturnType dispatchOp(TAR& target, TOOL& tool) + { + return Dispatcher::call (tool, target); + } + }; + + +/** 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); } + + + + + + // ================================================================== Library ==== + + 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 Visionary : public Leader + { + }; + + + class VerboseVisitor + : public Tool + { + protected: + void talk_to (string guy) + { + cout << format ("Hello %s, nice to meet you...\n") % guy; + } + }; + + class Babbler + : public VerboseVisitor, + public Applicable, + public Applicable + { + public: + void treat (Boss&) { talk_to("Boss"); } + void treat (BigBoss&) { talk_to("big Boss"); } + }; + + + + + + + + /************************************************************************* + * @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 + *
  • calling the correct visiting tool specialized function + * for given concrete hierarchy classes
  • + *
  • visiting tool not declaring to visit some class
  • + *
  • newly added class causes the catch-all to be invoked + * when visited by known visitor
  • + *
+ */ + class VisitingTool_concept : public Test + { + virtual void run(Arg arg) + { + known_visitor_known_class(); + visitor_not_visiting_some_class(); + } + + 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; + Visitor& vista (bab); + 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; + Visitor& vista (bab); + homo1.apply (vista); // doesn't visit HomoSapiens + homo2.apply (vista); // treats Visionary as Boss + } + + }; + + + /** Register this test class... */ + LAUNCHER (VisitingTool_concept, "unit common"); + + + + } // namespace test + + } // namespace visitor + +} // namespace cinelerra diff --git a/tests/components/common/visitingtooltest.cpp b/tests/components/common/visitingtooltest.cpp index bc81f53cd..1607ba963 100644 --- a/tests/components/common/visitingtooltest.cpp +++ b/tests/components/common/visitingtooltest.cpp @@ -91,6 +91,7 @@ namespace cinelerra public: void treat (BigBoss&) { talk_to("big Boss"); } void treat (HomoSapiens&) { talk_to("we-do-everything-for-YOU"); } ///< catch-all function + void catchy(HomoSapiens&) {} }; diff --git a/tests/components/proc/mobject/builder/buildertooltest.cpp b/tests/components/proc/mobject/builder/buildertooltest.cpp index cbe68007e..6b0dadd01 100644 --- a/tests/components/proc/mobject/builder/buildertooltest.cpp +++ b/tests/components/proc/mobject/builder/buildertooltest.cpp @@ -76,10 +76,12 @@ namespace mobject }; class TestTool : public BuilderTool, - public Applicable + public Applicable, + public Applicable { void treat (Clip& c) { cout << "media is: "<< str(c.getMedia()) <<"\n"; } void treat (Buildable&){ cout << "catch-all-function called.\n"; } + void treat (AbstractMO&){ cout << "catch-all-MO.\n"; } }; TestTool t1;