LUMIERA.clone/tests/components/proc/mobject/builder/buildertooltest.cpp
Ichthyostega 88fc2f6099 WIP reworked to replace boost::variant by a custom solution
Not a big simplification, but at least the actual codepath is shorter,
while it's not so general as boost::variant (and not threadsafe!)
2008-05-17 04:34:46 +02:00

682 lines
18 KiB
C++

/*
BuilderTool(Test) - specialized visitor used within the builder for processing Placements
Copyright (C) Lumiera.org
2008, 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.
* *****************************************************/
#include "common/test/run.hpp"
#include "proc/mobject/builder/buildertool.hpp"
#include "proc/asset/category.hpp"
#include "proc/asset/media.hpp"
#include "proc/mobject/session/clip.hpp"
#include "proc/asset.hpp"
#include "common/p.hpp"
#include "proc/mobject/placement.hpp"
#include "proc/mobject/explicitplacement.hpp" //////////TODO
#include "common/typelistutil.hpp"
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <boost/type_traits/is_polymorphic.hpp>
#include <boost/type_traits/is_base_of.hpp>
//#include <boost/variant.hpp>
#include <iostream>
using std::string;
using std::cout;
namespace mobject
{
DEFINE_SPECIALIZED_PLACEMENT (session::AbstractMO);
namespace builder
{
namespace test
{
using session::Clip;
using session::AbstractMO;
/////////////////////////////////////////////////////TODO: move to buildertool.hpp
using lumiera::P;
// Problem
/*
- brauche Laufzeittyp des Zielobjektes
- an wen wird die Dispatcher-Tabelle gebunden?
- falls an den Wrapper: Gefahr, zur Laufzeit nicht die Tabelle zu bekommen, in die das Trampolin registriert wurde
- falls an das Zielobjekt: wie gebe ich Referenz und konkreten Typ des Wrappers an den Funktionsaufruf im Tool?
- vieleicht einen allgemeinen Argument-Adapter nutzen?
- Zielobjekt oder Wrapper<Zielobjekt> als Argumenttyp?
*/
using boost::enable_if;
using boost::is_convertible;
using lumiera::typelist::Types;
using lumiera::typelist::Node;
using lumiera::typelist::NullType;
// using lumiera::typelist::count;
// using lumiera::typelist::maxSize;
using lumiera::typelist::InstantiateChained;
template
< class TYPES // List of Types
, template<class,class,uint> class _X_ // your-template-goes-here
, class BASE = NullType // Base class at end of chain
, uint i = 0 // incremented on each instantiaton
>
class InstantiateWithIndex;
template< template<class,class,uint> class _X_
, class BASE
, uint i
>
class InstantiateWithIndex<NullType, _X_, BASE, i>
: public BASE
{
public:
typedef BASE Unit;
typedef NullType Next;
enum{ COUNT = i };
};
template
< class TY, typename TYPES
, template<class,class,uint> class _X_
, class BASE
, uint i
>
class InstantiateWithIndex<Node<TY, TYPES>, _X_, BASE, i>
: public _X_< TY
, InstantiateWithIndex<TYPES, _X_, BASE, i+1 >
, i
>
{
public:
typedef InstantiateWithIndex<TYPES,_X_,BASE,i+1> Next;
typedef _X_<TY,Next,i> Unit;
enum{ COUNT = Next::COUNT };
};
template<class TYPES>
struct count;
template<>
struct count<NullType>
{
enum{ value = 0 };
};
template<class TY, class TYPES>
struct count<Node<TY,TYPES> >
{
enum{ value = 1 + count<TYPES>::value };
};
template<class TYPES>
struct maxSize;
template<>
struct maxSize<NullType>
{
enum{ value = 0 };
};
template<class TY, class TYPES>
struct maxSize<Node<TY,TYPES> >
{
enum{ nextval = maxSize<TYPES>::value
, thisval = sizeof(TY)
, value = nextval > thisval? nextval:thisval
};
};
template<class T>
struct Holder
{
T content;
Holder (T const& src) : T(src) {}
template<typename TAR>
TAR get () { return static_cast<TAR> (content); }
};
typedef Types < Placement<MObject>*
, P<asset::Asset>*
> ::List
WrapperTypes;
template<typename X>
struct EmptyVal
{
static X create()
{
return X();
}
};
template<typename X>
struct EmptyVal<X*&>
{
static X*& create()
{
static X* null(0);
return null;
}
};
const uint TYPECNT = count<WrapperTypes>::value;
const size_t SIZE = maxSize<WrapperTypes>::value;
struct Buffer
{
char buffer_[SIZE];
uint which;
Buffer() : which(TYPECNT) {}
void*
put (void)
{
deleteCurrent();
return 0;
}
void
deleteCurrent (); // depends on the Deleter, see below
};
template<typename T, class BASE, uint idx>
struct Storage : BASE
{
T&
put (T const& toStore)
{
BASE::deleteCurrent(); // remove old content, if any
T& storedObj = *new(BASE::buffer_) T (toStore);
this->which = idx; // remember the actual type selected
return storedObj;
}
using BASE::put;
};
template<class FUNCTOR>
struct CaseSelect
{
typedef typename FUNCTOR::Ret Ret;
typedef Ret (*Func)(Buffer&);
Func table_[TYPECNT];
CaseSelect ()
{
for (uint i=0; i<TYPECNT; ++i)
table_[i] = 0;
}
template<typename T>
static Ret
trampoline (Buffer& storage)
{
T& content = reinterpret_cast<T&> (storage.buffer_);
return FUNCTOR::access (content);
}
template<typename T>
void
create_thunk (uint idx)
{
Func thunk = &trampoline<T>;
table_[idx] = thunk;
}
Ret
invoke (Buffer& storage)
{
if (TYPECNT <= storage.which)
return FUNCTOR::ifEmpty ();
else
return (*table_[storage.which]) (storage);
}
};
template< class T, class BASE, uint i >
struct CasePrepare
: BASE
{
CasePrepare () : BASE()
{
// BASE::table_[i] = &(BASE::template trampoline<T>);
BASE::template create_thunk<T>(i);
}
};
template<class FUNCTOR>
typename FUNCTOR::Ret
access (Buffer& buf)
{
typedef InstantiateWithIndex< WrapperTypes
, CasePrepare
, CaseSelect<FUNCTOR>
>
Accessor;
static Accessor select_case;
return select_case.invoke(buf);
}
struct Deleter
{
typedef void Ret;
template<typename T>
static void access (T& elem) { elem.~T(); }
static void ifEmpty () { }
};
void
Buffer::deleteCurrent ()
{
access<Deleter>(*this); // remove old content, if any
which = TYPECNT; // mark as empty
}
using boost::remove_pointer;
using boost::remove_reference;
using boost::is_convertible;
using boost::is_polymorphic;
using boost::is_base_of;
using boost::enable_if;
template <typename SRC, typename TAR>
struct can_cast : boost::false_type {};
template <typename SRC, typename TAR>
struct can_cast<SRC*,TAR*> { enum { value = is_base_of<SRC,TAR>::value };};
template <typename SRC, typename TAR>
struct can_cast<SRC*&,TAR*> { enum { value = is_base_of<SRC,TAR>::value };};
template <typename SRC, typename TAR>
struct can_cast<SRC&,TAR&> { enum { value = is_base_of<SRC,TAR>::value };};
template <typename T>
struct has_RTTI
{
typedef typename remove_pointer<
typename remove_reference<T>::type>::type TPlain;
enum { value = is_polymorphic<TPlain>::value };
};
template <typename SRC, typename TAR>
struct use_dynamic_downcast
{
enum { value = can_cast<SRC,TAR>::value
&& has_RTTI<SRC>::value
&& has_RTTI<TAR>::value
};
};
template <typename SRC, typename TAR>
struct use_static_downcast
{
enum { value = can_cast<SRC,TAR>::value
&& ( !has_RTTI<SRC>::value
|| !has_RTTI<TAR>::value
)
};
};
template <typename SRC, typename TAR>
struct use_conversion
{
enum { value = is_convertible<SRC,TAR>::value
&& !( use_static_downcast<SRC,TAR>::value
||use_dynamic_downcast<SRC,TAR>::value
)
};
};
template<typename RET>
struct NullAccessor
{
typedef RET Ret;
static RET access (...) { return ifEmpty(); }
static RET ifEmpty () { return EmptyVal<RET>::create(); }
};
template<typename TAR>
struct AccessCasted : NullAccessor<TAR>
{
using NullAccessor<TAR>::access;
template<typename ELM>
static typename enable_if< use_dynamic_downcast<ELM&,TAR>, TAR>::type
access (ELM& elem)
{
return dynamic_cast<TAR> (elem);
}
template<typename ELM>
static typename enable_if< use_static_downcast<ELM&,TAR>, TAR>::type
access (ELM& elem)
{
return static_cast<TAR> (elem);
}
template<typename ELM>
static typename enable_if< use_conversion<ELM&,TAR>, TAR>::type
access (ELM& elem)
{
return elem;
}
};
/**
* helper to treat various sorts of smart-ptrs uniformly.
* Implemented as a variant-type value object, it is preconfigured
* with the possible hierarchy-base classes used within this application.
* Thus, when passing in an arbitrary smart-ptr, the best fitting smart-ptr
* type pointing to the corresponding base class is selected for internal storage.
* Later on, stored values can be retrieved either utilitzing static or dynamic casts;
* error reporting is similar to the bahaviour of dynamic_cast<T>: when retrieving
* a pointer, in case of mismatch NULL is returned.
*/
class WrapperPtr // NONCOPYABLE!!
{
private:
typedef InstantiateWithIndex< WrapperTypes
, Storage
, Buffer
>
VariantHolder;
/** storage: buffer holding either and "empty" marker,
* or one of the configured pointer to wrapper types */
VariantHolder holder_;
public:
void
reset ()
{
access<Deleter> (holder_);
holder_.which = TYPECNT;
}
template<typename WRA>
WrapperPtr&
operator= (WRA* src) ///< store a ptr to the given wrapper, after casting to base
{
if (src) holder_.put (src);
else reset();
return *this;
}
template<typename WRA>
WRA*
get () ///< retrieve ptr and try to downcast to type WRA
{
typedef AccessCasted<WRA*> AccessWraP;
WRA* res = access<AccessWraP>(this->holder_);
return res;
}
};
class BuTuul
: public lumiera::visitor::Tool<void, InvokeCatchAllFunction>
{
WrapperPtr currentWrapper_;
public:
template<template<class> class WRA, class TAR>
void rememberWrapper (WRA<TAR>* argument)
{
currentWrapper_ = argument;
}
void forgetWrapper ()
{
currentWrapper_.reset();
}
template<class TAR>
Placement<TAR>&
getPlacement ()
{
Placement<TAR>* pPlacement = currentWrapper_.get<Placement<TAR> >();
return *pPlacement;
}
ExplicitPlacement
getExplicitPlacement ()
{
return getPlacement<MObject>().resolve();
}
template<class TAR>
P<TAR>
getPtr ()
{
P<TAR>* pP = currentWrapper_.get<P<TAR> >();
return *pP;
}
};
template
< class TOOLImpl, // concrete BuilderTool implementation
class TYPELIST // list of all concrete Buildables to be treated
>
class Appli
: public lumiera::visitor::Applicable<TOOLImpl, TYPELIST, BuTuul>
{ }
;
using lumiera::typelist::Types; // convienience for the users of "Applicable"
class BDable : public lumiera::visitor::Visitable<BuTuul>
{
};
/////////////////////////////////////////////////////TODO: move to buildertool.hpp
class DummyMO : public AbstractMO, public BDable
{
public:
DummyMO() { };
virtual bool isValid() const { return true;}
// DEFINE_PROCESSABLE_BY (BuilderTool);
static void killDummy (MObject* dum) { delete (DummyMO*)dum; }
virtual void
apply (BuTuul& tool)
{
return BDable::dispatchOp (*this, tool);
}
};
class Clop : public Clip, public BDable
{
Clop (Clip& base) : Clip (base.clipDef_, base.mediaDef_) {}
virtual void
apply (BuTuul& tool)
{
return BDable::dispatchOp (*this, tool);
}
};
template<typename WRA>
inline BDable::ReturnType
apply (BuTuul& tool, WRA& wrappedTargetObj)
{
WRA* ptr = &wrappedTargetObj;
(*ptr)->isValid();
tool.rememberWrapper(ptr);
wrappedTargetObj->apply (tool);
tool.forgetWrapper();
}
//////////////////////////////////////////////////////TODO: wip-wip
class DummyPlacement : public Placement<AbstractMO>
{
public:
DummyPlacement()
: Placement<AbstractMO>::Placement(*new DummyMO, &DummyMO::killDummy)
{ }
};
//////////////////////////////////////////////////////TODO: wip-wip
class TestTool
: public Appli<TestTool, Types<Clip, AbstractMO>::List>
{
public:
void treat (Clip& c)
{
Placement<Clip>& pC = getPlacement<Clip>();
cout << "media is: "<< str(pC->getMedia()) <<"\n"
<< "Placement(adr) " << &pC <<"\n";
}
void treat (AbstractMO&)
{
Placement<AbstractMO>& placement = getPlacement<AbstractMO>();
cout << "unspecific MO; Placement(adr) " << &placement <<"\n";
}
void onUnknown (Buildable&)
{
cout << "catch-all-function called.\n";
}
};
/*******************************************************************
* @test the generic visitor pattern specialized for treating
* 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.
* \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)
{
TestTool t1;
BuTuul& tool (t1);
DummyPlacement dumm;
Placement<AbstractMO>& dummy(dumm);
Placement<Clip> clip = asset::Media::create("test-1", asset::VIDEO)->createClip();
cout << "Placement(adr) " << &clip <<"\n";
apply (tool, clip);
cout << "Placement(adr) " << &dumm <<"\n";
apply (tool, dummy);
}
};
/** Register this test class... */
LAUNCHER (BuilderTool_test, "unit builder");
} // namespace test
} // namespace builder
//////////////////////////////////////////////////////TODO: wip-wip
/* template<>
ExplicitPlacement
Placement<session::AbstractMO>::resolve () const
{
UNIMPLEMENTED ("just a test");
}
*/ //////////////////////////////////////////////////////TODO: wip-wip
} // namespace mobject