covered the more esoteric cases for visiting tools and made BuilderTool work
This commit is contained in:
parent
78a4bf8e6b
commit
89c9d2f3df
10 changed files with 173 additions and 41 deletions
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ namespace mobject
|
|||
|
||||
virtual Time& getLength() { return length; }
|
||||
|
||||
DEFINE_PROCESSABLE_BY (builder::BuilderTool);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -81,6 +81,8 @@ namespace mobject
|
|||
*/
|
||||
PClipAsset findClipAsset () const;
|
||||
|
||||
DEFINE_PROCESSABLE_BY (builder::BuilderTool);
|
||||
|
||||
};
|
||||
|
||||
typedef Placement<Clip> PClipMO;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue