moved new code into library modules and adapted BuilderTool to use it.

BuilderTool_test now passed.
This commit is contained in:
Fischlurch 2008-05-19 08:38:13 +02:00
parent 662678f8d1
commit 86162ad314
20 changed files with 953 additions and 709 deletions

168
src/common/accesscasted.hpp Normal file
View file

@ -0,0 +1,168 @@
/*
ACCESSCASTED.hpp - util template to access a value using conversion or cast as appropriate
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.
*/
/** @file accesscasted.hpp
** Helper for accessing a value employing either conversion or downcast
** depending on the relation of the source type (type of the original value)
** and the target type (type we need within the usage context).
** When instantiating AcessCasted<TAR>, we get a template static function
** \c AcessCasted<TAR>::access<SRC>(SRC& elm), but the actual implementation
** is choosed using boost::type_traits. If no viable implementatino can be
** selected, \c EmptyVal<TAR>::create() is invoked instead, which by default
** creates a NULL value or similar by using the no-argument ctor of the
** type TAR. Alternatively, you may define an specialisation fo EmptyVal,
** e.g. throwing an exception instead of creating a NULL value.
**
** @see lumiera::WrapperPtr usage example to access a variant record
**
*/
#ifndef UTIL_ACCESSCASTED_H
#define UTIL_ACCESSCASTED_H
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <boost/type_traits/is_polymorphic.hpp>
#include <boost/type_traits/is_base_of.hpp>
namespace util {
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 X>
struct EmptyVal
{
static X create() { return X(); }
};
template<typename X>
struct EmptyVal<X*&>
{
static X*& create() { static X* nullP(0); return nullP; }
};
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;
}
};
} // namespace util
#endif

View file

@ -40,7 +40,10 @@ This code is heavily inspired by
/** @file typelistutil.hpp
** Helpers for working with lumiera::typelist::Types (i.e. lists-of-types).
**
** The main purpose is to build interfaces and polymorphic implementations
** (using virtual functions) based on templated Types or Collections of types,
** which is not possible without Template Metaprogrmming.
**
** @see lumiera::query::ConfigRules usage example
** @see typelist.hpp
**
@ -141,6 +144,94 @@ namespace lumiera
};
/**
* A Variation of InstantiateChained providing an incremented
* Index value template parameter. This index can e.g. be used
* to store pointers in a dispatcher table in the Base class.
* Similar to InstantiateChained, this template builds a linear
* chain of inheritance. The user-provided template, which is
* to be instantiated for all types in the Typelist, now has to
* accept an additional third parameter (uint i).
*/
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 };
};
/**
* Metafunction counting the number of Types in the collection
*/
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 };
};
/**
* Metafunction " max( sizeof(T) ) for T in TYPES "
*/
template<class TYPES>
struct maxSize;
template<>
struct maxSize<NullType>
{
enum{ value = 0 };
};
template<class TY, class TYPES>
struct maxSize<Node<TY,TYPES> >
{
enum{ thisval = sizeof(TY)
, nextval = maxSize<TYPES>::value
, value = nextval > thisval? nextval:thisval
};
};
} // namespace typelist
} // namespace lumiera

276
src/common/variant.hpp Normal file
View file

@ -0,0 +1,276 @@
/*
VARIANT.hpp - simple variant wrapper (typesafe union)
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.
*/
/** @file variant.hpp
** This file defines a simple alternative to boost::variant.
** It pulls in fewer headers and has a shorter code path, but also
** doesn't deal with alignement issues and is <b>not threadsafe</b>.
**
** Values can be stored using \c operator= . In order to access the value
** stored in lumiera::Variant, you additionally need to define a "functor"
** <ul><li>with a typedef "Ret" for the return type</li>
** <li>providing a <tt>static Ret access(ELM&)</tt> function
** for each of the types used in the Variant</li>
** </ul>
**
** @see wrapperptr.hpp usage example
*/
#ifndef LUMIERA_VARIANT_H
#define LUMIERA_VARIANT_H
#include "common/typelistutil.hpp"
#include <boost/noncopyable.hpp>
namespace lumiera {
namespace variant {
using lumiera::typelist::count;
using lumiera::typelist::maxSize;
using lumiera::typelist::InstantiateWithIndex;
/**
* internal helper used to build a variant storage wrapper.
* Parametrized with a collection of types, it provides functionality
* to copy a value of one of these types into an internal buffer, while
* remembering which of these types was used to place this copy.
* The value can be later on extracted using a visitation like mechanism,
* which takes a functor class and invokes a function \c access(T&) with
* the type matching the current value in storage
*/
template<typename TYPES>
struct Holder
{
enum { TYPECNT = count<TYPES>::value
, SIZE = maxSize<TYPES>::value
};
/** Storage to hold the actual 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 PlacementAdapter : BASE
{
T&
put (T const& toStore)
{
BASE::deleteCurrent(); // remove old content, if any
T& storedObj = *new(BASE::buffer_) T (toStore);
BASE::which_ = idx; // remember the actual type selected
return storedObj;
}
using BASE::put;
};
typedef InstantiateWithIndex< TYPES
, PlacementAdapter
, Buffer
>
Storage;
/** provide a dispatcher table based visitation mechanism */
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);
}
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>;
}
};
template<class FUNCTOR>
static typename FUNCTOR::Ret
access (Buffer& buf)
{
typedef InstantiateWithIndex< TYPES
, 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 () { }
};
};
template<typename TYPES>
inline void
Holder<TYPES>::Buffer::deleteCurrent ()
{
access<Deleter>(*this); // remove old content, if any
which_ = TYPECNT; // mark as empty
}
} // namespace variant
/**
* A variant wrapper (typesafe union) capable of holding a value of any
* of a bounded collection of types. The value is stored in a local buffer
* directly within the object and may be accessed by a typesafe visitation.
*
* \par
* This utility class is similar to boost::variant and indeed was implemented
* (5/08) in an effort to replace the latter in a draft solution for the problem
* of typesafe access to the correct wrapper class from within some builder tool.
* Well -- after finisihng this "exercise" I must admit that it is not really
* much more simple than what boost::variant does internally. At least we are
* pulling in fewer headers and the actual code path is shorter compared with
* boost::variant, at the price of beeing not so generic, not caring for
* alignment issues within the buffer and being <b>not threadsafe</b>
*
* @param TYPES collection of possible types to be stored in this variant object
* @param Access policy how to access the stored value
*/
template< typename TYPES
, template<typename> class Access
>
class Variant
: boost::noncopyable
{
typedef variant::Holder<TYPES> Holder;
typedef typename Holder::Deleter Deleter;
/** storage: buffer holding either and "empty" marker,
* or one of the configured pointer to wrapper types */
typename Holder::Storage holder_;
public:
void reset () { holder_.deleteCurrent();}
/** store a copy of the given argument within the
* variant holder buffer, thereby typically casting
* or converting the given source type to the best
* suited (base) type (out of the collection of possible
* types for this Variant instance)
*/
template<typename SRC>
Variant&
operator= (SRC src)
{
if (src) holder_.put (src); // see Holder::PlacementAdaptor::put
else reset();
return *this;
}
/** retrieve current content of the variant,
* trying to cast or convert it to the given type.
* Actually, the function \c access(T&) on the
* Access-policy (template param) is invoked with the
* type currently stored in the holder buffer.
* May return NULL if conversion fails.
*/
template<typename TAR>
TAR
get ()
{
typedef Access<TAR> Extractor;
return Holder::template access<Extractor> (this->holder_);
}
};
} // namespace lumiera
#endif

63
src/common/wrapperptr.hpp Normal file
View file

@ -0,0 +1,63 @@
/*
WRAPPERPTR.hpp - variant record able to hold a pointer to some smart-ptr/wrapper types, providing conversions
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.
*/
#ifndef LUMIERA_WRAPPERPTR_H
#define LUMIERA_WRAPPERPTR_H
#include "common/variant.hpp"
#include "common/accesscasted.hpp"
#include "common/typelist.hpp"
#include "proc/mobject/placement.hpp"
#include "common/p.hpp"
namespace asset { class Asset; }
namespace mobject { class MObject; }
namespace lumiera {
typedef typelist::Types < mobject::Placement<mobject::MObject>*
, P<asset::Asset>*
> ::List
WrapperTypes;
/**
* 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.
*/
typedef lumiera::Variant<WrapperTypes, util::AccessCasted> WrapperPtr;
} // namespace lumiera
#endif

View file

@ -0,0 +1,70 @@
/*
ApplicableBuilderTargetTypes - definitinon header specifying all types treated by builder tools
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.
*/
#ifndef MOBJECT_BUILDER_APPLICABLEBUILDERTARGETTYPES_H
#define MOBJECT_BUILDER_APPLICABLEBUILDERTARGETTYPES_H
#include "proc/mobject/builder/buildertool.hpp"
namespace mobject {
namespace session {
class Clip;
class Effect;
class AbstractMO;
template<class VAL> class Auto;
// Forward declarations sufficient here...
// actual definitions necessary only in the
// implementation file (*cpp) of the builder tool.
}
namespace builder {
typedef Types< session::Clip,
session::Effect,
session::AbstractMO
> ::List
BuilderTargetTypes;
/**
* Marker used to declare some visiting Tool class to be actually a
* mobject::builder::BuilderTool and to possibly accept and treat the
* common selection of types to be handled by any such builder tool.
* The actual BuilderTool impl should inherit from this template by
* feeding back its type (CRTP), this causes a dispatcher table entry
* be generated for this concrete BuilderTool implementation.
*/
template<class TOOL>
struct ApplicableBuilderTargetTypes
: Applicable<TOOL, BuilderTargetTypes>
{ };
} // namespace mobject::builder
} // namespace mobject
#endif

View file

@ -20,20 +20,54 @@
*/
/** @file buildertool.hpp
** Visiting-tool mechanism configured specifically for the Builder.
** The Builder creates the render nodes network by applying several Builder Tools
** to the objects found in the Session, EDL and Fixture. These BuilderTool instances
** contain the details of the builder implementation.
**
** As the objects to be treated are normally handled by smart-ptrs, BuilderTool provides
** a special facility for dealing with these wrapped objects. There are some liabilities.
** <ul><li>each concrete Buildable subtype to be treated specifically needs to
** declare \c DEFINE_PROCESSABLE_BY(BuilderTool) </li>
** <li>at the same time, the concrete BuilderTool subclass has to declare
** being Applicable to this concrete Buildable subtype. The recommended way
** of ensuring this, is to add an entry to applicablebuildertargettypes.hpp
** and then derive the concrete BuilderTool subclass from
** ApplicableBuilderTargetTypes</li>
** <li>when accessing the wrapper from within a \c treat() function, a suitable
** concrete wrapper type has to be specified. If the wrapper type used for
** invoking the BuilderTool (function \c apply(BuilderTool&l, WrappedObject&) )
** can not be converted to this type requested from within the call, an
** assertion failure (or segmentation fault in a release build) will result.</li>
** </ul>
**
** @see visitor.hpp
** @see applicablebuildertargettypes.hpp
** @see buildertooltest.hpp
** @see nodecreatertool.hpp
*/
#ifndef MOBJECT_BUILDER_TOOL_H
#define MOBJECT_BUILDER_TOOL_H
#include "common/visitor.hpp"
#include "common/wrapperptr.hpp"
#include "common/p.hpp"
#include "proc/mobject/placement.hpp"
#include "proc/mobject/explicitplacement.hpp"
namespace mobject {
namespace mobject
{
class Buildable;
namespace builder
{
namespace builder {
using lumiera::P;
/**
* Policy invoking an catch-all function for processing
@ -49,19 +83,79 @@ namespace mobject
virtual RET onUnknown (Buildable& target) = 0;
};
/**
* Base class of all BuilderTools, used according to the visitor pattern:
* each Tool contains the concrete implementation for one task to be done
* to the various MObject classes. The concrete builder tool implementation
* should not diretcly inherit from this base interface, but rather through
* an instantiation of the "Applicable" template parametrized with all
* concrete Buildable classes, for which it wants calls to be dispatched.
* concrete Buildable classes, for which it wants calls to be dispatched.
* \par
* In addition to lumiera::visitor::Tool, BuilderTool adds support for dealing
* with objects normally handled by means of smart-pointers or similar
* wrappers, most notably mobject::Placement. The visitaion is initiated
* by calling the stand-alone function \c (BuilderTool& tool, WRA& wrappedTargetObj) ,
* which forwards to the visitation mechanism supported by the type contained
* in the wrapper, but stores away a pointer to the wrapped object, which can
* be retrieved in a typesafe manner from within the \c treat(ConcreteType&)
* function.
* @note retrieving the wrapper is <b>not threadsafe</b> and <b>not reentrant</b>,
* as we simply store a pointer within the BuilderTool instance.
*/
typedef lumiera::visitor::Tool<void, InvokeCatchAllFunction> BuilderTool;
class BuilderTool
: public lumiera::visitor::Tool<void, InvokeCatchAllFunction>
{
lumiera::WrapperPtr currentWrapper_;
public:
template<template<class> class WRA, class TAR>
void rememberWrapper (WRA<TAR>* ptr_toWrappedTarget)
{
currentWrapper_ = ptr_toWrappedTarget;
}
void forgetWrapper ()
{
currentWrapper_.reset();
}
protected: /* == interface for accessing the wrapper from within tool application == */
template<class TAR>
Placement<TAR>&
getPlacement ()
{
Placement<TAR>* pPlacement = currentWrapper_.get<Placement<TAR>*>();
ENSURE (pPlacement, "wrong target type when invoking %s", __PRETTY_FUNCTION__);
return *pPlacement;
}
ExplicitPlacement
getExplicitPlacement ()
{
return getPlacement<MObject>().resolve();
}
template<class TAR>
lumiera::P<TAR>
getPtr ()
{
P<TAR>* pP = currentWrapper_.get<P<TAR>*>();
ENSURE (pP, "wrong target type when invoking %s", __PRETTY_FUNCTION__);
return *pP;
}
};
/**
* declare the concrete types a BuilderTool may recievee and treat.
* @note it is recommended to use ApplicableBuilderTargetTypes
*/
template
< class TOOLImpl, // concrete BuilderTool implementation
class TYPELIST // list of all concrete Buildables to be treated
@ -71,17 +165,33 @@ namespace mobject
{ }
;
using lumiera::typelist::Types; // convienience for the users of "Applicable"
using lumiera::typelist::Types; // convenience for the users of "Applicable"
} // namespace mobject::builder
/**
* Marker Interface for classes Visitable by Builder tools.
* Marker Interface for classes visitable by Builder tools.
*/
class Buildable : public lumiera::visitor::Visitable<builder::BuilderTool>
{ };
namespace builder { // to be found by ADL
template<typename WRA>
inline Buildable::ReturnType
apply (BuilderTool& tool, WRA& wrappedTargetObj)
{
tool.rememberWrapper(&wrappedTargetObj);
wrappedTargetObj->apply (tool); // dispatch to suitable treat() function
tool.forgetWrapper();
}
} // namespace mobject::builder
} // namespace mobject
#endif

View file

@ -31,11 +31,8 @@ using mobject::session::Clip;
using mobject::session::Effect;
using mobject::session::Auto;
namespace mobject
{
namespace builder
{
namespace mobject {
namespace builder {

View file

@ -24,23 +24,15 @@
#ifndef MOBJECT_BUILDER_NODECREATERTOOL_H
#define MOBJECT_BUILDER_NODECREATERTOOL_H
#include "proc/mobject/builder/buildertool.hpp"
#include "proc/mobject/builder/applicablebuildertargettypes.hpp"
#include "proc/engine/processor.hpp"
namespace mobject
{
namespace session
{
// Forward declarations
class Effect;
class Clip;
template<class VAL> class Auto;
}
namespace builder
{
namespace mobject {
namespace builder {
@ -52,11 +44,10 @@ namespace mobject
* render engine under construction such as to reflect the properties
* of the MObject in the actual render.
*/
class NodeCreatorTool : public BuilderTool
class NodeCreatorTool
: public ApplicableBuilderTargetTypes<NodeCreatorTool>
{
//////////////////////////////////////////////TODO: switch to acyclic visitior!!!!!!!!!!!!!
public:
virtual void treat (mobject::session::Clip& clip) ;
virtual void treat (mobject::session::Effect& effect) ;

View file

@ -31,10 +31,8 @@ using mobject::session::Clip;
using mobject::session::Effect;
namespace mobject
{
namespace builder
{
namespace mobject {
namespace builder {

View file

@ -24,26 +24,18 @@
#ifndef MOBJECT_BUILDER_SEGMENTATIONTOOL_H
#define MOBJECT_BUILDER_SEGMENTATIONTOOL_H
#include <list>
#include "proc/mobject/builder/buildertool.hpp"
#include "proc/mobject/builder/applicablebuildertargettypes.hpp"
#include "proc/mobject/session/segment.hpp"
#include <list>
using std::list;
namespace mobject
{
namespace session
{
// Forward declarations
class Clip;
class Effect;
}
namespace builder
{
namespace mobject {
namespace builder {
/**
@ -53,10 +45,10 @@ namespace mobject
* can be represented by automation solely, without the need
* to change the node connections.
*/
class SegmentationTool : public BuilderTool
class SegmentationTool
: public ApplicableBuilderTargetTypes<SegmentationTool>
{
//////////////////////////////////////////////TODO: switch to acyclic visitior!!!!!!!!!!!!!
public:
void treat (mobject::session::Clip& clip) ;

View file

@ -30,9 +30,9 @@
namespace mobject
{
/**
* Special kind of Placement, where the location of the
* MObject has been nailed down to a fixed position.
@ -51,7 +51,7 @@ namespace mobject
const Pipe pipe;
typedef std::pair<Time,Pipe> SolutionData; //TODO (ichthyo consideres better passing of solution by subclass)
/** no need to resolve any further, as this ExplicitPlacement
* already \e is the result of a resolve()-call.
*/
@ -61,9 +61,7 @@ namespace mobject
return *this;
}
/** */ /////////////////////////////////////////////////////////////TODO: wip-wip...
DEFINE_PROCESSABLE_BY (builder::BuilderTool);
protected:
/* @todo ichthyo considers a much more elegant implementation utilizing a subclass
* of FixedLocation, which would serve as Placement::LocatingSolution, and
@ -82,8 +80,8 @@ namespace mobject
/** copying prohibited, ExplicitPlacement is effectively const! */
ExplicitPlacement& operator= (const ExplicitPlacement&);
};
} // namespace mobject
#endif

View file

@ -88,9 +88,10 @@ namespace mobject
virtual bool operator== (const MObject& oo) const =0;
};
typedef Placement<MObject> PMO;

View file

@ -23,25 +23,39 @@
#include "proc/mobject/placement.hpp"
#include "proc/mobject/explicitplacement.hpp"
#include "proc/mobject/mobject.hpp"
#include <boost/format.hpp>
using boost::str;
namespace mobject
{
/** @note we know we need only this single
* specialisation, because we define
* the Placements of more specific
* MObject kinds to be subclasses
* of Placement<MObject>, so they
* will inherit this function.
*/
template<>
template<class MO>
ExplicitPlacement
Placement<MObject>::resolve () const
Placement<MO>::resolve () const
{
return ExplicitPlacement (*this, chain.resolve());
}
/** @note we know we need only this single specialisation,
* because we define the Placements of more specific
* MObject kinds to be subclasses of Placement<MObject>,
* so they will inherit this function.
*/
template ExplicitPlacement Placement<MObject>::resolve() const;
template<>
Placement<MObject>::operator string () const
{
static boost::format fmt("Placement<%s> %|50T.| use-cnt=%x adr=%x pointee=%x");
return str(fmt % typeid(*get()).name() % use_count() % this % get() );
}
} // namespace mobject

View file

@ -58,12 +58,10 @@
#define MOBJECT_PLACEMENT_H
#include "pre.hpp"
#include "proc/mobject/mobject.hpp"
#include "proc/mobject/session/locatingpin.hpp"
#include "proc/asset/pipe.hpp"
#include <tr1/memory>
using std::tr1::shared_ptr;
namespace mobject
@ -72,6 +70,7 @@ namespace mobject
class ExplicitPlacement;
using std::tr1::shared_ptr;
@ -82,9 +81,10 @@ namespace mobject
* be within the Session/EDL
*/
template<class MO>
class Placement : public Buildable, protected shared_ptr<MO>
class Placement : protected shared_ptr<MO>
{
protected:
typedef shared_ptr<MO> Base;
typedef lumiera::Time Time;
typedef asset::shared_ptr<asset::Pipe> Pipe;
@ -99,13 +99,14 @@ namespace mobject
operator-> () const
{
ENSURE (*this);
return shared_ptr<MO>::operator-> ();
return Base::operator-> ();
}
operator string() const ;
size_t use_count() const { return Base::use_count(); }
virtual ~Placement() {};
/** */ /////////////////////////////////////////////////////////////TODO: totmachen?
DEFINE_PROCESSABLE_BY (builder::BuilderTool);
/** interface for defining the kind of placement
@ -128,20 +129,19 @@ namespace mobject
friend class session::MObjectFactory;
};
typedef Placement<MObject> PMO;
/* === defining specialisations to be subclasses === */
#define DEFINE_SPECIALIZED_PLACEMENT(SUBCLASS) \
#define DEFINE_SPECIALIZED_PLACEMENT(SUBCLASS, BASE) \
template<> \
class Placement<SUBCLASS> : public Placement<MObject> \
class Placement<SUBCLASS> : public Placement<BASE> \
{ \
protected: \
Placement (SUBCLASS & m, void (*moKiller)(MObject*)) \
: Placement<MObject>::Placement (m, moKiller) \
: Placement<BASE>::Placement (m, moKiller) \
{ }; \
friend class session::MObjectFactory; \
\
@ -154,7 +154,6 @@ namespace mobject
(shared_ptr<MObject>::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<MObject>!

View file

@ -66,7 +66,7 @@ namespace mobject
and the unlink() function of the asset should take it into
account when breaking circular references.
*/
public: ////////////////////////////////////TODO: temporarily for buildertooltest
const Media & mediaDef_;
const asset::Clip & clipDef_;
@ -99,7 +99,7 @@ public: ////////////////////////////////////TODO: temporarily for buildertoolt
} // namespace mobject::session
/** Placement<Clip> defined to be subclass of Placement<MObject> */
DEFINE_SPECIALIZED_PLACEMENT (session::Clip);
DEFINE_SPECIALIZED_PLACEMENT (session::Clip, MObject);
} // namespace mobject

View file

@ -51,7 +51,7 @@ namespace mobject
} // namespace mobject::session
/** Placement<Effect> defined to be subclass of Placement<MObject> */
DEFINE_SPECIALIZED_PLACEMENT (session::Effect);
DEFINE_SPECIALIZED_PLACEMENT (session::Effect, MObject);
} // namespace mobject
#endif

View file

@ -50,7 +50,7 @@ namespace mobject
} // namespace mobject::session
/** Placement<Meta> defined to be subclass of Placement<MObject> */
DEFINE_SPECIALIZED_PLACEMENT (session::Meta);
DEFINE_SPECIALIZED_PLACEMENT (session::Meta, MObject);
} // namespace mobject
#endif

View file

@ -83,7 +83,7 @@ namespace mobject
} // namespace mobject::session
/** Placement<Track> defined to be subclass of Placement<MObject> */
DEFINE_SPECIALIZED_PLACEMENT (session::Track);
DEFINE_SPECIALIZED_PLACEMENT (session::Track, session::Meta);
} // namespace mobject
#endif

View file

@ -4,8 +4,12 @@ TESTING "Component Test Suite: Builder" ./test-components --group=builder
TEST "BuilderTool_test" BuilderTool_test <<END
out: media is: Asset(VIDEO:lumi.test-1 v1)
out: catch-all-function called.
out: apply (tool, clip);
out: Clip on media : Asset(VIDEO:lumi.test-1 v1)
out: apply (tool, test1);
out: treat (AbstractMO&);
out: apply (tool, test2);
out: catch-all-function called...
END

View file

@ -22,654 +22,127 @@
#include "common/test/run.hpp"
#include "proc/mobject/builder/buildertool.hpp"
#include "proc/mobject/placement.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/noncopyable.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 "common/util.hpp"
#include <iostream>
using util::cStr;
using std::string;
using std::cout;
namespace util {
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 X>
struct EmptyVal
{
static X create() { return X(); }
};
template<typename X>
struct EmptyVal<X*&>
{
static X*& create() { static X* nullP(0); return nullP; }
};
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;
}
};
}
namespace lumiera {
namespace typelist {
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
};
};
} // namespace typelist
namespace variant {
using lumiera::typelist::count;
using lumiera::typelist::maxSize;
using lumiera::typelist::InstantiateWithIndex;
/**
* internal helper used to build a variant storage wrapper.
* Parametrized with a collection of types, it provides functionality
* to copy a value of one of these types into an internal buffer, while
* remembering which of these types was used to place this copy.
* The value can be later on extracted using a visitation like mechanism,
* which takes a functor object and invokes a function \c access(T&) with
* the type matching the current value in storage
*/
template<typename TYPES>
struct Holder
{
enum { TYPECNT = count<TYPES>::value
, SIZE = maxSize<TYPES>::value
};
/** Storage to hold the actual 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 PlacementAdapter : 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;
};
typedef InstantiateWithIndex< TYPES
, PlacementAdapter
, Buffer
>
Storage;
/** provide a dispatcher table based visitation mechanism */
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);
}
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>;
}
};
template<class FUNCTOR>
static typename FUNCTOR::Ret
access (Buffer& buf)
{
typedef InstantiateWithIndex< TYPES
, 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 () { }
};
};
template<typename TYPES>
void
Holder<TYPES>::Buffer::deleteCurrent ()
{
access<Deleter>(*this); // remove old content, if any
which = TYPECNT; // mark as empty
}
} // namespace variant
/**
* A variant wrapper (typesafe union) capable of holding a value of any
* of a bounded collection of types. The value is stored in a local buffer
* directly within the object and may be accessed by a typesafe visitation.
*
* \par
* This utility class is similar to boost::variant and indeed was implemented
* (5/08) in an effort to replace the latter in a draft solution for the problem
* of typesafe access to the correct wrapper class from within some builder tool.
* Well -- after finisihng this "exercise" I must admit that it is not really
* much more simple than what boost::variant does internally. At least we are
* pulling in fewer headers and the actual code path is shorter compared with
* boost::variant, at the price of beeing not so generic, not caring for
* alignment issues within the buffer and being <b>not threadsafe</b>
*
* @param TYPES collection of possible types to be stored in this variant object
* @param Access policy how to access the stored value
*/
template< typename TYPES
, template<typename> class Access = util::AccessCasted
>
class Variant
: boost::noncopyable
{
typedef variant::Holder<TYPES> Holder;
typedef typename Holder::Deleter Deleter;
/** storage: buffer holding either and "empty" marker,
* or one of the configured pointer to wrapper types */
typename Holder::Storage holder_;
public:
void reset () { holder_.deleteCurrent();}
/** store a copy of the given argument within the
* variant holder buffer, thereby typically casting
* or converting the given source type to the best
* suited (base) type (out of the collection of possible
* types for this Variant instance)
*/
template<typename SRC>
Variant&
operator= (SRC src)
{
if (src) holder_.put (src); // see Holder::PlacementAdaptor::put
else reset();
return *this;
}
/** retrieve current content of the variant,
* trying to cast or convert it to the given type.
* Actually, the function \c access(T&) on the
* Access-policy (template param) is invoked with the
* type currently stored in the holder buffer.
* May return NULL if conversion fails.
*/
template<typename TAR>
TAR
get ()
{
typedef Access<TAR> Extractor;
return Holder::template access<Extractor> (this->holder_);
}
};
} // namespace lumiera
namespace mobject
{
DEFINE_SPECIALIZED_PLACEMENT (session::AbstractMO);
namespace builder
{
namespace test
{
namespace mobject {
namespace builder {
namespace test {
using session::Clip;
using session::AbstractMO;
/////////////////////////////////////////////////////TODO: move to buildertool.hpp
using lumiera::P;
using lumiera::typelist::Types;
typedef Types < Placement<MObject>*
, P<asset::Asset>*
> ::List
WrapperTypes;
/**
* 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.
*/
typedef lumiera::Variant<WrapperTypes> WrapperPtr;
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
/**
* Test MObject subclass, which, contrary to any real MObject,
* can be created directly without involving MObjectFactory.
*/
class TestMO : public AbstractMO
{
public:
DummyMO() { };
virtual bool isValid() const { return true;}
// DEFINE_PROCESSABLE_BY (BuilderTool);
TestMO() { };
static void killDummy (MObject* dum) { delete (DummyMO*)dum; }
virtual void
apply (BuTuul& tool)
{
return BDable::dispatchOp (*this, tool);
}
DEFINE_PROCESSABLE_BY (BuilderTool);
virtual bool isValid() const { return true;}
static void killDummy (MObject* dum) { delete (TestMO*)dum; }
};
class Clop : public Clip, public BDable
{
Clop (Clip& base) : Clip (base.clipDef_, base.mediaDef_) {}
virtual void
apply (BuTuul& tool)
{
return BDable::dispatchOp (*this, tool);
}
/**
* Subclass-1 is \em not defined "processable",
* thus will always be handled as TestMO...
*/
class TestSubMO1 : public TestMO
{ };
/**
* Subclass-2 \em is defined "processable",
* but we omit the necessary "applicable" definition in TestTool,
* resulting in an invocation of the error (catch-all) function...
*/
class TestSubMO2 : public TestMO
{
DEFINE_PROCESSABLE_BY (BuilderTool);
};
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>
template<class MO>
class TestPlacement : public Placement<MO>
{
public:
DummyPlacement()
: Placement<AbstractMO>::Placement(*new DummyMO, &DummyMO::killDummy)
TestPlacement(TestMO& testObject)
: Placement<MO>::Placement(testObject, &TestMO::killDummy)
{ }
};
//////////////////////////////////////////////////////TODO: wip-wip
/**
* BuilderTool implementation for checking the invokation of the correct
* \c treat() function and for accessing the original Placement from
* within this invocation. It is declared to be applicable to Clip
* and TestMO objects (wrapped into any acceptable shared-ptr).
* Intentionally, we omit to declare it applicable to TestSubMO2 instances.
* In reality this would be a case of misconfiguration, because TestSubMO2
* is defined to be processable and consequently has an \apply() function,
* which, due to this ommission can't find a dispatcher entry when invoked,
* so it will call the \c onUnknown(Buildable&) instead
*/
class TestTool
: public Appli<TestTool, Types<Clip, AbstractMO>::List>
: public Applicable<TestTool, Types<Clip, TestMO>::List>
{
public:
string log_;
void treat (Clip& c)
{
Placement<Clip>& pC = getPlacement<Clip>();
cout << "media is: "<< str(pC->getMedia()) <<"\n"
<< "Placement(adr) " << &pC <<"\n";
cout << "Clip on media : "<< str(pC->getMedia()) <<"\n";
log_ = string (pC);
}
void treat (AbstractMO&)
{
Placement<AbstractMO>& placement = getPlacement<AbstractMO>();
cout << "unspecific MO; Placement(adr) " << &placement <<"\n";
cout << "treat (AbstractMO&);\n";
log_ = string (getPlacement<MObject>());
}
void onUnknown (Buildable&)
void onUnknown (Buildable& bxx)
{
cout << "catch-all-function called.\n";
cout << "catch-all-function called...\n";
log_ = string (getPlacement<MObject>());
}
};
/*******************************************************************
* @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
/*************************************************************************************
* @test the generic visitor pattern specialized for treating MObjects in the builder.
* 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...)
* we create 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.
* But from within the \c treat() function, we may access the wrapper object
* (i.e. shared_ptr, or lumiera::P or Placement) used when invoking the
* BuilderTool by using the protected interface on BuilderTool.
*
* @see VisitingTool_test for checking general visitor functionality
*/
class BuilderTool_test : public Test
{
@ -677,18 +150,27 @@ namespace mobject
{
TestTool t1;
BuTuul& tool (t1);
BuilderTool& tool = t1;
DummyPlacement dumm;
Placement<AbstractMO>& dummy(dumm);
Placement<Clip> clip = asset::Media::create("test-1", asset::VIDEO)->createClip();
TestPlacement<MObject> test1(*new TestSubMO1);
TestPlacement<MObject> test2(*new TestSubMO2);
cout << "Placement(adr) " << &clip <<"\n";
cout << "apply (tool, clip);\n";
apply (tool, clip);
cout << "Placement(adr) " << &dumm <<"\n";
apply (tool, dummy);
INFO (test, "got Wrapper = %s", t1.log_.c_str());
ASSERT (t1.log_ == string(clip));
cout << "apply (tool, test1);\n";
apply (tool, test1);
INFO (test, "got Wrapper = %s", t1.log_.c_str());
ASSERT (t1.log_ == string(test1));
cout << "apply (tool, test2);\n";
apply (tool, test2);
INFO (test, "got Wrapper = %s", t1.log_.c_str());
ASSERT (t1.log_ == string(test2));
}
};
@ -702,14 +184,4 @@ namespace mobject
} // namespace builder
//////////////////////////////////////////////////////TODO: wip-wip
/* template<>
ExplicitPlacement
Placement<session::AbstractMO>::resolve () const
{
UNIMPLEMENTED ("just a test");
}
*/ //////////////////////////////////////////////////////TODO: wip-wip
} // namespace mobject