covered the more esoteric cases for visiting tools and made BuilderTool work

This commit is contained in:
Fischlurch 2007-11-29 07:07:14 +01:00
parent 78a4bf8e6b
commit 89c9d2f3df
10 changed files with 173 additions and 41 deletions

View file

@ -82,7 +82,7 @@ namespace cinelerra
template
< typename RET = void,
class TOOL = Tool,
template<typename,class> class ERR = UseDefault
template<typename,class,class> class ERR = UseDefault
>
class Visitable
{
@ -108,7 +108,7 @@ namespace cinelerra
return concreteTool->treat (target);
else
return ERR<RET,TOOL>::onUnknown (target,tool);
return ERR<RET,TAR,TOOL>::onUnknown (target,tool);
}
};

View file

@ -48,10 +48,9 @@ namespace cinelerra
* of encountering an unknown Visitor (typically caused by
* adding a new class to the visitable hierarchy)
*/
template<typename RET, class TOOL>
template<typename RET,class TAR,class TOOL>
struct UseDefault
{
template<class TAR>
static RET onUnknown (TAR&, TOOL&)
{
return RET();
@ -61,10 +60,9 @@ namespace cinelerra
/**
* Policy to throw when encountering an unknown visiting tool
*/
template<typename RET, class TOOL>
template<typename RET,class TAR,class TOOL>
struct ThrowException
{
template<class TAR>
static RET onUnknown (TAR&, TOOL&)
{
throw cinelerra::error::Config("unable to decide what tool operation to call");
@ -74,14 +72,15 @@ namespace cinelerra
/**
* Policy invoking an catch-all function for processing
* an unknown tool / target pair
* @note using this policy effectively enforces
* implementing a catch-all function \c treat(TAR&)
*/
template<typename RET, class TOOL>
template<typename RET,class TAR,class TOOL>
struct InvokeCatchAllFunction
{
template<class TAR>
static RET onUnknown (TAR& target,TOOL& tool)
{
target.apply (static_cast<TOOL&> (tool));
tool.treat (target);
}
};

View file

@ -309,7 +309,7 @@ namespace asset
/** convienient for debugging */
inline string str (PcAsset& a)
inline string str (const PcAsset& a)
{
if (a)
return string (*a.get());

View file

@ -29,6 +29,7 @@
#include "cinelerra.h"
#include "proc/mobject/buildable.hpp"
#include "proc/mobject/builder/buildertool.hpp"
#include "proc/mobject/placement.hpp"
#include "proc/asset.hpp" // TODO finally not needed?

View file

@ -47,6 +47,7 @@ namespace mobject
virtual Time& getLength() { return length; }
DEFINE_PROCESSABLE_BY (builder::BuilderTool);
};

View file

@ -81,6 +81,8 @@ namespace mobject
*/
PClipAsset findClipAsset () const;
DEFINE_PROCESSABLE_BY (builder::BuilderTool);
};
typedef Placement<Clip> PClipMO;

View file

@ -183,8 +183,17 @@ END
TEST "VisitingTool_test" VisitingTool_test <<END
out: === Babbler meets Boss and BigBoss ===
out: Hello Boss, nice to meet you...
out: Hello big Boss, nice to meet you...
out: === Babbler meets HomoSapiens and Leader ===
out: Hello Boss, nice to meet you...
out: === Blatherer meets Leader, Visionary and HomoSapiens masqueraded as HomoSapiens ===
out: === Blatherer meets Leader and Visionary masqueraded as Chief ===
out: Hello we-do-everything-for-YOU, nice to meet you...
out: Hello we-do-everything-for-YOU, nice to meet you...
out: === VerboseVistr masqueraded as Tool meets Leader and Visionary masqueraded as HomoSapiens ===
out: === Blatherer masqueraded as Tool meets Leader and Visionary masqueraded as Leader ===
return: 0
END

View file

@ -3,7 +3,9 @@ TESTING "Component Test Suite: Builder" ./test-components --group=builder
PLANNED "BuilderTool_test" BuilderTool_test <<END
TEST "BuilderTool_test" BuilderTool_test <<END
out: media is: Asset(VIDEO:cin3.test-1 v1)
out: catch-all-function called.
END

View file

@ -59,25 +59,61 @@ namespace cinelerra
public:
DEFINE_PROCESSABLE_BY (Tool);
};
class Leader : public Boss
{
};
class VerboseVisitor
: public Tool,
public Applicable<Boss>,
public Applicable<BigBoss>
: public Tool
{
protected:
void talk_to (string guy)
{
cout << format ("Hello %s, nice to meet you...\n") % guy;
}
};
class Babbler
: public VerboseVisitor,
public Applicable<Boss>,
public Applicable<BigBoss>
{
public:
void treat (Boss&) { talk_to("Boss"); }
void treat (BigBoss&) { talk_to("big Boss"); }
};
// the classes above comprise the standard use case,
// what follows are rather exotic corner cases
class Blatherer
: public VerboseVisitor,
public Applicable<BigBoss>
{
public:
void treat (BigBoss&) { talk_to("big Boss"); }
void treat (HomoSapiens&) { talk_to("we-do-everything-for-YOU"); } ///< catch-all function
};
typedef Visitable<void,Blatherer,InvokeCatchAllFunction> Vista2;
class Chief : public Vista2 ///< abstract intermeidary node
{
};
class Leader : public Chief,
public Boss ///< can act as HomoSapiens or as Chief
{
public:
using HomoSapiens::apply;
virtual void apply (Blatherer& tool) { return Vista2::dispatchOp (*this, tool); }
};
class Visionary : public Leader
{
};
@ -97,34 +133,83 @@ namespace cinelerra
virtual void run(Arg arg)
{
known_visitor_known_class();
TODO ("implement the more complicated visitor test cases");
//visitor_not_visiting_some_class();
//visitor_treating_new_subclass();
visitor_not_visiting_some_class();
visitor_treating_new_subclass();
}
void known_visitor_known_class()
{
HomoSapiens x1;
Boss x2;
BigBoss x3;
Boss x1;
BigBoss x2;
HomoSapiens& xx2 (x2);
HomoSapiens& xx3 (x3);
// masquerade as HomoSapiens...
HomoSapiens& homo1 (x1);
HomoSapiens& homo2 (x2);
VerboseVisitor wizzy;
x1.apply (wizzy);
xx2.apply (wizzy);
xx3.apply (wizzy);
cout << "=== Babbler meets Boss and BigBoss ===\n";
Babbler bab;
homo1.apply (bab);
homo2.apply (bab);
}
void visitor_not_visiting_some_class()
{
UNIMPLEMENTED ("testing the generic visitor pattern");
HomoSapiens x1;
Leader x2;
HomoSapiens& homo1 (x1);
HomoSapiens& homo2 (x2);
cout << "=== Babbler meets HomoSapiens and Leader ===\n";
Babbler bab;
homo1.apply (bab); // doesn't visit HomoSapiens
homo2.apply (bab); // treats Leader as Boss
}
void visitor_treating_new_subclass()
{
UNIMPLEMENTED ("testing the generic visitor pattern");
Leader x1;
Visionary x2;
HomoSapiens x3;
HomoSapiens& homo1 (x1);
HomoSapiens& homo2 (x2);
HomoSapiens& homo3 (x3);
Chief& chief1 (x1);
Chief& chief2 (x2);
Leader& lead1 (x1);
Leader& lead2 (x2);
Blatherer bla;
VerboseVisitor vista;
Tool& tool1 (vista);
Tool& tool2 (bla);
cout << "=== Blatherer meets Leader, Visionary and HomoSapiens masqueraded as HomoSapiens ===\n";
homo1.apply (bla); // nothing happens, because Blatherer doesn't declare to do anything as Tool
homo2.apply (bla);
homo3.apply (bla);
cout << "=== Blatherer meets Leader and Visionary masqueraded as Chief ===\n";
chief1.apply (bla); // but now, acting in the Chief hierarchy, the catch-all is called
chief2.apply (bla);
cout << "=== VerboseVistr masqueraded as Tool meets Leader and Visionary masqueraded as HomoSapiens ===\n";
homo1.apply (tool1); // because acting in the HomoSapiens hierarch, no visiting happens and no catch-all
homo2.apply (tool1);
cout << "=== Blatherer masqueraded as Tool meets Leader and Visionary masqueraded as Leader ===\n";
lead1.apply (tool2); // nothing happens, because Leader here is treated by his HomoSapiens base
lead2.apply (tool2);
// note: the following doesn't compile (an this is a feature, not a bug):
// "chief1.apply (tool2)" : because the "Chief"-hierarchy enforces the catch-all function
// and the compiler doesn't know Blatherer actually implements this
// catch-all-function, because of the masqueradeing as Tool. Note
// further: the catch-all function can have a more general type
// (in this case HomoSapiens instead of Chief)
// "Chief chief" : is abstract, because the Visitable-Template enforces implementing
// the "apply(TOOL&)" function, either directly or via the
// DEFINE_PROCESSABLE_BY macro
}
};

View file

@ -23,13 +23,11 @@
#include "common/test/run.hpp"
#include "proc/mobject/builder/buildertool.hpp"
//#include "common/factory.hpp"
//#include "common/util.hpp"
#include "proc/asset/category.hpp"
#include "proc/asset/media.hpp"
#include "proc/mobject/session/clip.hpp"
//#include <boost/format.hpp>
#include <iostream>
//using boost::format;
using std::string;
using std::cout;
@ -41,6 +39,11 @@ namespace mobject
namespace test
{
using session::Clip;
using session::AbstractMO;
using cinelerra::visitor::Applicable;
@ -49,14 +52,44 @@ namespace mobject
* MObjects in the builder. Because the generic visitor
* implementation is already covered by
* \link VisitingTool_test, it is sufficient to test
* the specialisation to the builder
* @todo work out what this means haha....
* the specialisation to the builder.
* \par
* Besides using existing MObject types (at the moment session::Clip),
* we create inline a yet-unknown new MObject subclass. When passing
* such to any BuilderTool subclass, the compiler enforces the definition
* of a catch-all function, which is called, when there is no other
* applicable \c treat(MO&) function. Note further, whithin the specific
* treat-functions we get direct references, whithout interfering with
* Placements and memory management. (Is this a good idea? it allows
* client code to bypass the Placement (Handle). At least, I think
* it is not really a problem...)
*/
class BuilderTool_test : public Test
{
virtual void run(Arg arg)
{
UNIMPLEMENTED ("testing the visitor pattern for the builder");
class DummyMO : public AbstractMO
{
public:
DummyMO() { };
virtual bool isValid() const { return true;}
};
class TestTool : public BuilderTool,
public Applicable<Clip>
{
void treat (Clip& c) { cout << "media is: "<< str(c.getMedia()) <<"\n"; }
void treat (Buildable&){ cout << "catch-all-function called.\n"; }
};
TestTool t1;
BuilderTool& tool (t1);
DummyMO dumm;
PMO clip = asset::Media::create("test-1", asset::VIDEO)->createClip();
clip->apply (tool);
dumm.apply (tool);
}
};