moved new code into library modules and adapted BuilderTool to use it.
BuilderTool_test now passed.
This commit is contained in:
parent
662678f8d1
commit
86162ad314
20 changed files with 953 additions and 709 deletions
168
src/common/accesscasted.hpp
Normal file
168
src/common/accesscasted.hpp
Normal 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
|
||||
|
|
@ -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
276
src/common/variant.hpp
Normal 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
63
src/common/wrapperptr.hpp
Normal 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
|
||||
70
src/proc/mobject/builder/applicablebuildertargettypes.hpp
Normal file
70
src/proc/mobject/builder/applicablebuildertargettypes.hpp
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -31,11 +31,8 @@ using mobject::session::Clip;
|
|||
using mobject::session::Effect;
|
||||
using mobject::session::Auto;
|
||||
|
||||
namespace mobject
|
||||
{
|
||||
|
||||
namespace builder
|
||||
{
|
||||
namespace mobject {
|
||||
namespace builder {
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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) ;
|
||||
|
|
|
|||
|
|
@ -31,10 +31,8 @@ using mobject::session::Clip;
|
|||
using mobject::session::Effect;
|
||||
|
||||
|
||||
namespace mobject
|
||||
{
|
||||
namespace builder
|
||||
{
|
||||
namespace mobject {
|
||||
namespace builder {
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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) ;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -88,9 +88,10 @@ namespace mobject
|
|||
virtual bool operator== (const MObject& oo) const =0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
typedef Placement<MObject> PMO;
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>!
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue