WIP further refactorings.

Put nodeoperation.hpp and ConfigSelector in a sub namespace engine::config
This commit is contained in:
Fischlurch 2008-08-03 16:47:38 +02:00
parent 847cc9c267
commit cf7bd178de
9 changed files with 589 additions and 425 deletions

View file

@ -25,12 +25,18 @@
** Under some circumstances it is necessary to assemble functionality
** out of elementary building blocks. Especially, this assembly can be
** expressed as template specialisations directed by a configuration type.
** Thus, the purpose of this header is to encode a flag-like configuraiton
** Thus, the purpose of this header is to encode flag-like configuraitons
** as distinct types, which can be used to select such specialisations.
** Each possible configuration can be encoded as a list of flags, which allows
** to generate, filter and process those configurations. The final goal is to
** automatically generate a factory which will deliver objects configured
** according to the configuration in question.
** automatically generate a factory which is able to deliver objects
** configured according to the situation encoded in the flags.
**
** @note currently there is an inherent limitation to configurations defined by
** a maximum of 5 independant flags. While it is easy to increase this limit,
** you should consider that the final goal is to genarate template instantiations,
** which would lead to more and more code bloat with growing number of possible
** combination.
**
** @see proc::engine::config::Strategy usage example
** @see proc::engine::config::ConfigSelector

View file

@ -27,15 +27,17 @@
namespace lumiera {
/* types for figuring out the overload resolution chosen by the compiler */
typedef char Yes_t;
struct No_t { char padding[8]; };
namespace typelist {
/* types for figuring out the overload resolution chosen by the compiler */
typedef char Yes_t;
struct No_t { char padding[8]; };
/** semi-automatic detection if an instantiation is possible.
* Requires help by the template to be tested, which needs to define
* a typedef member \c is_defined. The embedded metafunction Test can be used

View file

@ -41,6 +41,7 @@
#define LUMIERA_VARIANT_H
#include "common/meta/typelistutil.hpp"
#include "common/meta/generator.hpp"
#include <boost/noncopyable.hpp>

View file

@ -63,243 +63,249 @@
#include "proc/engine/bufftable.hpp"
#include "proc/engine/nodeinvocation.hpp"
#include "common/meta/util.hpp"
#include "common/meta/configflags.hpp"
namespace engine {
/**
* Collection of functions used to build up the invocation sequence.
*/
class OperationBase
{
};
namespace config {
template<class NEXT>
struct QueryCache : NEXT
{
BuffHandle
step (Invocation& ivo)
{
BuffHandle fetched = ivo.fetch (
this->genFrameID (ivo));
if (fetched)
return fetched;
else
return NEXT::step (ivo);
}
};
template<class NEXT>
struct AllocBufferTable : NEXT
{
BuffHandle
step (Invocation& ivo)
{
BuffTableChunk buffTab (ivo.wiring, ivo.getBuffTableStorage());
ivo.setBuffTab(&buffTab);
ASSERT (ivo.buffTab);
ASSERT (ivo.buffTab_isConsistent());
return NEXT::step (ivo);
}
};
template<class NEXT>
struct PullInput : NEXT
{
BuffHandle
step (Invocation& ivo)
{
BuffHandle * inH = ivo.buffTab->inHandle;
BuffHandle::PBuff *inBuff = ivo.buffTab->inBuff;
for (uint i = 0; i < ivo.nrI(); ++i )
{
inBuff[i] =
*(inH[i] = this->pullPredecessor(ivo,i)); // invoke predecessor
// now Input #i is ready...
}
return NEXT::step (ivo); // note: passing down a ref to the ProcessInvocation
}
};
template<class NEXT>
struct ReadSource : NEXT
{
BuffHandle
step (Invocation& ivo)
{
BuffHandle *inH = ivo.buffTab->inHandle;
BuffHandle *outH = ivo.buffTab->outHandle;
BuffHandle::PBuff *inBuff = ivo.buffTab->inBuff;
BuffHandle::PBuff *outBuff = ivo.buffTab->outBuff;
ASSERT (ivo.nrO() == ivo.nrI() );
for (uint i = 0; i < ivo.nrI(); ++i )
{
inBuff[i] = outBuff[i] =
*(inH[i] = outH[i] = this->getSource(ivo,i));
// now Input #i is ready...
}
return NEXT::step (ivo);
}
};
template<class NEXT>
struct AllocOutput : NEXT
{
BuffHandle
step (Invocation& ivo)
{
ASSERT (ivo.buffTab);
ASSERT (ivo.nrO() < ivo.buffTabSize());
BuffHandle *outH = ivo.buffTab->outHandle;
BuffHandle::PBuff *outBuff = ivo.buffTab->outBuff;
for (uint i = 0; i < ivo.nrO(); ++i )
{
outBuff[i] =
*(outH[i] = ivo.allocateBuffer (ivo.wiring.out[i].bufferType));
// now Output buffer for channel #i is available...
}
return NEXT::step (ivo);
}
};
template<class NEXT>
struct ProcessData : NEXT
{
BuffHandle
step (Invocation& ivo)
{
ASSERT (ivo.buffTab);
ASSERT (ivo.buffTab_isConsistent());
ASSERT (this->validateBuffers(ivo));
// Invoke our own process() function,
// providing the array of outBuffer+inBuffer ptrs
ivo.wiring.processFunction (ivo.buffTab->outBuff);
return NEXT::step (ivo);
}
};
template<class NEXT>
struct FeedCache : NEXT
{
BuffHandle
step (Invocation& ivo)
{
for (uint i = 0; i < ivo.nrO(); ++i )
{
// declare all Outputs as finished
ivo.is_calculated(ivo.buffTab->outHandle[i]);
}
return NEXT::step (ivo);
}
};
template<class NEXT>
struct ReleaseBuffers : NEXT /////////////////TODO: couldn't this be done automatically by BuffTab's dtor??
{ ///////////////// this would require BuffHandle to be a smart ref....
BuffHandle
step (Invocation& ivo)
{
// all buffers besides the required Output no longer needed
this->releaseBuffers(ivo.buffTab->outHandle,
ivo.buffTabSize(),
ivo.outNr);
return ivo.buffTab->outHandle[ivo.outNr];
}
};
/* === declare the possible Assembly of these elementary steps === */
enum Cases
{
CACHING = 1,
PROCESS,
INPLACE,
/**
* Collection of functions used to build up the invocation sequence.
*/
class OperationBase
{
typedef lumiera::Yes_t is_defined;
};
NOT_SET = 0,
NUM_Cases = INPLACE
};
template<class NEXT>
struct QueryCache : NEXT
{
BuffHandle
step (Invocation& ivo)
{
BuffHandle fetched = ivo.fetch (
this->genFrameID (ivo));
if (fetched)
return fetched;
else
return NEXT::step (ivo);
}
};
template<class NEXT>
struct AllocBufferTable : NEXT
{
BuffHandle
step (Invocation& ivo)
{
BuffTableChunk buffTab (ivo.wiring, ivo.getBuffTableStorage());
ivo.setBuffTab(&buffTab);
ASSERT (ivo.buffTab);
ASSERT (ivo.buffTab_isConsistent());
return NEXT::step (ivo);
}
};
template<class NEXT>
struct PullInput : NEXT
{
BuffHandle
step (Invocation& ivo)
{
BuffHandle * inH = ivo.buffTab->inHandle;
BuffHandle::PBuff *inBuff = ivo.buffTab->inBuff;
for (uint i = 0; i < ivo.nrI(); ++i )
{
inBuff[i] =
*(inH[i] = this->pullPredecessor(ivo,i)); // invoke predecessor
// now Input #i is ready...
}
return NEXT::step (ivo); // note: passing down a ref to the ProcessInvocation
}
};
template<class NEXT>
struct ReadSource : NEXT
{
BuffHandle
step (Invocation& ivo)
{
BuffHandle *inH = ivo.buffTab->inHandle;
BuffHandle *outH = ivo.buffTab->outHandle;
BuffHandle::PBuff *inBuff = ivo.buffTab->inBuff;
BuffHandle::PBuff *outBuff = ivo.buffTab->outBuff;
ASSERT (ivo.nrO() == ivo.nrI() );
for (uint i = 0; i < ivo.nrI(); ++i )
{
inBuff[i] = outBuff[i] =
*(inH[i] = outH[i] = this->getSource(ivo,i));
// now Input #i is ready...
}
return NEXT::step (ivo);
}
};
template<class NEXT>
struct AllocOutput : NEXT
{
BuffHandle
step (Invocation& ivo)
{
ASSERT (ivo.buffTab);
ASSERT (ivo.nrO() < ivo.buffTabSize());
BuffHandle *outH = ivo.buffTab->outHandle;
BuffHandle::PBuff *outBuff = ivo.buffTab->outBuff;
for (uint i = 0; i < ivo.nrO(); ++i )
{
outBuff[i] =
*(outH[i] = ivo.allocateBuffer (ivo.wiring.out[i].bufferType));
// now Output buffer for channel #i is available...
}
return NEXT::step (ivo);
}
};
template<class NEXT>
struct ProcessData : NEXT
{
BuffHandle
step (Invocation& ivo)
{
ASSERT (ivo.buffTab);
ASSERT (ivo.buffTab_isConsistent());
ASSERT (this->validateBuffers(ivo));
// Invoke our own process() function,
// providing the array of outBuffer+inBuffer ptrs
ivo.wiring.processFunction (ivo.buffTab->outBuff);
return NEXT::step (ivo);
}
};
template<class NEXT>
struct FeedCache : NEXT
{
BuffHandle
step (Invocation& ivo)
{
for (uint i = 0; i < ivo.nrO(); ++i )
{
// declare all Outputs as finished
ivo.is_calculated(ivo.buffTab->outHandle[i]);
}
return NEXT::step (ivo);
}
};
template<class NEXT>
struct ReleaseBuffers : NEXT /////////////////TODO: couldn't this be done automatically by BuffTab's dtor??
{ ///////////////// this would require BuffHandle to be a smart ref....
BuffHandle
step (Invocation& ivo)
{
// all buffers besides the required Output no longer needed
this->releaseBuffers(ivo.buffTab->outHandle,
ivo.buffTabSize(),
ivo.outNr);
return ivo.buffTab->outHandle[ivo.outNr];
}
};
/* === declare the possible Assembly of these elementary steps === */
enum Cases
{
CACHING = 1,
PROCESS,
INPLACE,
NOT_SET = 0,
NUM_Cases = INPLACE
};
template<char CACHE_Fl=0, char INPLACE_Fl=0>
struct SelectBuffProvider;
template<> struct SelectBuffProvider<CACHING> : AllocBufferFromCache { };
template<> struct SelectBuffProvider<NOT_SET,INPLACE> : AllocBufferFromParent{ };
template<> struct SelectBuffProvider<CACHING,INPLACE> : AllocBufferFromCache { };
template<> struct SelectBuffProvider<> : AllocBufferFromParent{ };
template<class Config>
struct Strategy ;
template<char CACHE_Fl=0, char INPLACE_Fl=0>
struct SelectBuffProvider;
template<> struct SelectBuffProvider<CACHING> : AllocBufferFromCache { };
template<> struct SelectBuffProvider<NOT_SET,INPLACE> : AllocBufferFromParent{ };
template<> struct SelectBuffProvider<CACHING,INPLACE> : AllocBufferFromCache { };
template<> struct SelectBuffProvider<> : AllocBufferFromParent{ };
template<class Config>
struct Strategy ;
using lumiera::typelist::Config;
template<char INPLACE>
struct Strategy< Config<CACHING,PROCESS,INPLACE> >
: QueryCache<
AllocBufferTable<
PullInput<
AllocOutput<
ProcessData<
FeedCache<
using lumiera::typelist::Config;
template<char INPLACE>
struct Strategy< Config<CACHING,PROCESS,INPLACE> >
: QueryCache<
AllocBufferTable<
PullInput<
AllocOutput<
ProcessData<
FeedCache<
ReleaseBuffers<
OperationBase > > > > > > >
{ };
template<char INPLACE>
struct Strategy< Config<PROCESS,INPLACE> >
: AllocBufferTable<
PullInput<
AllocOutput<
ProcessData<
ReleaseBuffers<
OperationBase > > > > > > >
{ };
template<char INPLACE>
struct Strategy< Config<PROCESS,INPLACE> >
: AllocBufferTable<
PullInput<
AllocOutput<
ProcessData<
OperationBase > > > > >
{ };
template<>
struct Strategy< Config<> >
: AllocBufferTable<
ReadSource<
ReleaseBuffers<
OperationBase > > > > >
{ };
template<>
struct Strategy< Config<> >
: AllocBufferTable<
ReadSource<
ReleaseBuffers<
OperationBase > > >
{ };
template<>
struct Strategy< Config<INPLACE> > : Strategy< Config<> > { };
template<>
struct Strategy< Config<CACHING> >
: AllocBufferTable<
ReadSource<
AllocOutput<
ProcessData< // wiring_.processFunction is supposed to do just buffer copying here
ReleaseBuffers<
OperationBase > > > > >
{ };
OperationBase > > >
{ };
template<>
struct Strategy< Config<INPLACE> > : Strategy< Config<> > { };
template<>
struct Strategy< Config<CACHING> >
: AllocBufferTable<
ReadSource<
AllocOutput<
ProcessData< // wiring_.processFunction is supposed to do just buffer copying here
ReleaseBuffers<
OperationBase > > > > >
{ };
} // namespace config
} // namespace engine
#endif

View file

@ -26,9 +26,77 @@
#include "proc/engine/nodeoperation.hpp"
#include "proc/engine/nodewiringconfig.hpp"
#include "common/meta/typelistutil.hpp"
namespace engine {
namespace { // internal: setting up a factory for each required configuration
using config::CACHING;
using config::PROCESS;
using config::INPLACE;
using config::ConfigSelector;
using config::Strategy;
using lumiera::typelist::Flags;
using lumiera::typelist::CombineFlags;
using lumiera::typelist::DefineConfigByFlags;
using lumiera::typelist::Instantiation;
using lumiera::typelist::Apply;
using lumiera::typelist::Filter;
typedef Flags<CACHING,PROCESS,INPLACE>::Tuple AllFlags;
/** build the list of all possible flag combinations */
typedef CombineFlags<AllFlags> AllFlagCombinations;
/** build a configuration type for each of those flag combinations */
typedef Apply<AllFlagCombinations::List, DefineConfigByFlags> AllConfigs;
/** filter those configurations which actually define a wiring strategy */
typedef Filter<AllConfigs::List, Instantiation<Strategy>::Test> PossibleConfigs;
class Alloc {}; ///////////////TODO
template<class CONF>
class WiringDescriptorFactory
{
Alloc& alloc_;
public:
WiringDescriptorFactory(Alloc& a)
: alloc_(a) {}
WiringDescriptor&
operator() ()
{
/////////////////////////////////////////////TODO
//return offset_ + Maybe<CONF>::CODE;
}
};
typedef ConfigSelector<WiringDescriptorFactory, WiringDescriptor&, Alloc&> WiringSelector;
struct WiringFactoryImpl
{
// WiringSelector selector;
WiringFactoryImpl (Alloc& alloc)
// : selector(PossibleConfigs::List(), alloc)
{ }
};
} // (END) internals
/////////////////////////////TODO: define the ctor
/** create and configure a concrete wiring descriptor to tie
* a ProcNode to its predecessor nodes. This includes selecting
@ -47,7 +115,8 @@ namespace engine {
UNIMPLEMENTED ("build the actual wiring descriptor based on given operation options");
// Bits config (FlagInfo<Config>::CODE);
// return selector(config);
size_t config = 13; /////////////////////////////////////////TODO
// return pImpl_->selector(config);
}
// BlockAlloc<NodeWiring< StateAdapter< Config<cache, process, inplace> > > >::fabricate();

View file

@ -27,6 +27,7 @@
#include "proc/engine/procnode.hpp"
#include <boost/scoped_ptr.hpp>
#include <cstddef>
@ -36,6 +37,8 @@ namespace engine {
class WiringFactory;
namespace { class WiringFactoryImpl; }
/**
* Actual implementation of the link between nodes,
@ -73,6 +76,8 @@ namespace engine {
class WiringFactory
{
boost::scoped_ptr<WiringFactoryImpl> pImpl_;
public:
WiringDescriptor&
operator() (uint nrOut, uint nrIn, bool cache);

View file

@ -56,122 +56,125 @@
namespace engine {
using lumiera::typelist::FlagInfo;
namespace config {
using boost::lexical_cast;
using util::contains;
/**
* Helper for fabricating ProcNode Wiring configurations.
* This object builds a table of factories, holding one factory
* for each possible node configuration. Provided with the desired
* configuration encoded as bits, the related factory can be invoked,
* thus producing a product object for the given configuration.
*
* \par implementation notes
* The actual factory class is templated, so it will be defined
* at the use site of ConfigSelector. Moreover, this factory
* usually expects an ctor argument, which will be fed through
* when creating the ConfigSelector instance. This is one of
* the reasons why we go through all these complicated factory
* building: this ctor argument usually brings in a reference
* to the actual memory allocator. Thus we have to rebuild the
* ConfigSelector each time we switch and rebuild the ProcNode
* factories, which in turn happens each time we use a new
* bulk allocation memory block -- typically for each separate
* segment of the Timeline and processing node graph.
*
* Now the selection of the possible flag configurations, for which
* Factory instances are created in the table, is governed by the
* type parameter of the ConfigSelector ctor. This type parameter
* needs to be a Typelist of Typelists, each representing a flag
* configuration. The intention is to to drive this selection by
* the use of template metaprogramming for extracting all
* currently defined StateProxy object configurations.
* @todo as the facories live only within the enclosed table (map)
* we could allocate them in-place. Unfortunately this is
* non-trivial, because the stl containers employ
* value semantics and thus do a copy even on insert.
* Thus, for now we use a shared_ptr to hold the factory
* heap allocated.
*/
template< template<class CONF> class Factory
, typename PAR ///< ctor parameter of the Factories
, typename RET ///< common base class of the Factory's products
>
class ConfigSelector
{
struct FacFunctor
{
virtual ~FacFunctor() {}
virtual RET invoke() =0;
};
template<class FAC>
struct FactoryHolder : FacFunctor
{
FAC factory_;
FactoryHolder(PAR p) : factory_(p) {}
virtual RET invoke () { return factory_(); }
};
typedef std::tr1::shared_ptr<FacFunctor> PFunc;
typedef std::map<size_t, PFunc> ConfigTable;
ConfigTable possibleConfig_; ///< Table of factories
/** Helper: a visitor usable with FlagInfo */
struct FactoryTableBuilder
{
PAR ctor_param_;
ConfigTable& factories_;
FactoryTableBuilder (ConfigTable& tab, PAR p)
: ctor_param_(p),
factories_(tab) { }
/* === visitation interface === */
typedef void Ret;
template<class CONF>
void
visit (size_t code)
{
PFunc pFactory (new FactoryHolder<Factory<CONF> > (ctor_param_));
factories_[code] = pFactory;
}
void done() {}
};
public:
template<class CONFS>
ConfigSelector(CONFS const&, PAR factory_ctor_param)
{
FactoryTableBuilder buildTable(this->possibleConfig_,
factory_ctor_param );
// store a new Factory instance
// for each possible Flag-Configuration
FlagInfo<CONFS>::accept (buildTable);
}
RET
operator() (size_t configFlags) ///< invoke the factory corresponding to the given config
{
if (contains (possibleConfig_, configFlags))
return possibleConfig_[configFlags]->invoke();
else
throw lumiera::error::Invalid("ConfigSelector: No preconfigured factory for config-bits="+lexical_cast<std::string>(configFlags));
}
};
using lumiera::typelist::FlagInfo;
using boost::lexical_cast;
using util::contains;
/**
* Helper for fabricating ProcNode Wiring configurations.
* This object builds a table of factories, holding one factory
* for each possible node configuration. Provided with the desired
* configuration encoded as bits, the related factory can be invoked,
* thus producing a product object for the given configuration.
*
* \par implementation notes
* The actual factory class is templated, so it will be defined
* at the use site of ConfigSelector. Moreover, this factory
* usually expects an ctor argument, which will be fed through
* when creating the ConfigSelector instance. This is one of
* the reasons why we go through all these complicated factory
* building: this ctor argument usually brings in a reference
* to the actual memory allocator. Thus we have to rebuild the
* ConfigSelector each time we switch and rebuild the ProcNode
* factories, which in turn happens each time we use a new
* bulk allocation memory block -- typically for each separate
* segment of the Timeline and processing node graph.
*
* Now the selection of the possible flag configurations, for which
* Factory instances are created in the table, is governed by the
* type parameter of the ConfigSelector ctor. This type parameter
* needs to be a Typelist of Typelists, each representing a flag
* configuration. The intention is to to drive this selection by
* the use of template metaprogramming for extracting all
* currently defined StateProxy object configurations.
* @todo as the facories live only within the enclosed table (map)
* we could allocate them in-place. Unfortunately this is
* non-trivial, because the stl containers employ
* value semantics and thus do a copy even on insert.
* Thus, for now we use a shared_ptr to hold the factory
* heap allocated.
*/
template< template<class CONF> class Factory
, typename PAR ///< ctor parameter of the Factories
, typename RET ///< common base class of the Factory's products
>
class ConfigSelector
{
struct FacFunctor
{
virtual ~FacFunctor() {}
virtual RET invoke() =0;
};
template<class FAC>
struct FactoryHolder : FacFunctor
{
FAC factory_;
FactoryHolder(PAR p) : factory_(p) {}
virtual RET invoke () { return factory_(); }
};
typedef std::tr1::shared_ptr<FacFunctor> PFunc;
typedef std::map<size_t, PFunc> ConfigTable;
ConfigTable possibleConfig_; ///< Table of factories
/** Helper: a visitor usable with FlagInfo */
struct FactoryTableBuilder
{
PAR ctor_param_;
ConfigTable& factories_;
FactoryTableBuilder (ConfigTable& tab, PAR p)
: ctor_param_(p),
factories_(tab) { }
/* === visitation interface === */
typedef void Ret;
template<class CONF>
void
visit (size_t code)
{
PFunc pFactory (new FactoryHolder<Factory<CONF> > (ctor_param_));
factories_[code] = pFactory;
}
void done() {}
};
public:
template<class CONFS>
ConfigSelector(CONFS const&, PAR factory_ctor_param)
{
FactoryTableBuilder buildTable(this->possibleConfig_,
factory_ctor_param );
// store a new Factory instance
// for each possible Flag-Configuration
FlagInfo<CONFS>::accept (buildTable);
}
RET
operator() (size_t configFlags) ///< invoke the factory corresponding to the given config
{
if (contains (possibleConfig_, configFlags))
return possibleConfig_[configFlags]->invoke();
else
throw lumiera::error::Invalid("ConfigSelector: No preconfigured factory for config-bits="+lexical_cast<std::string>(configFlags));
}
};
} // namespace config
} // namespace engine
#endif

View file

@ -38,8 +38,11 @@
#include "common/test/run.hpp"
#include "common/meta/util.hpp"
#include "common/meta/generator.hpp"
#include "common/meta/typelistutil.hpp"
#include "common/meta/configflags.hpp"
#include "meta/typelistdiagnostics.hpp"
#include "proc/engine/nodewiringconfig.hpp"
#include "common/util.hpp"
@ -69,77 +72,6 @@ namespace lumiera {
, NOT_SET = 0
};
format fmt ("-<%u>%s");
struct NullP
{
static string print () { return "-"; }
};
/** debugging template,
* printing the "number" used for intantiation on ctor call
*/
template<class NUM=NullType, class BASE=NullP>
struct Printer;
template<class BASE>
struct Printer<NullType, BASE>
: BASE
{
static string print () { return str( fmt % "·" % BASE::print()); }
};
template<class BASE, char Fl>
struct Printer<Flag<Fl>, BASE>
: BASE
{
static string print () { return str( fmt % uint(Fl) % BASE::print()); }
};
/** call the debug-print for a typelist
* utilizing the Printer template */
template<class L>
string
printSublist ()
{
typedef InstantiateChained<L, Printer, NullP> SubList;
return SubList::print();
}
/** Spezialisation for debug-printing of a nested sublist */
template<class TY, class TYPES, class BASE>
struct Printer<Node<TY,TYPES>, BASE>
: BASE
{
static string print ()
{
typedef Node<TY,TYPES> List;
return string("\n\t+--") + printSublist<List>()+"+"
+ BASE::print();
}
};
template<char f1, char f2, char f3, char f4, char f5, class BASE>
struct Printer<Config<f1,f2,f3,f4,f5>, BASE>
: BASE
{
static string print ()
{
typedef typename Config<f1,f2,f3,f4,f5>::Flags FlagList;
return string("\n\t+-Conf-[") + printSublist<FlagList>()+"]"
+ BASE::print();
}
};
#define DIAGNOSE(LIST) \
typedef InstantiateChained<LIST::List, Printer, NullP> Contents_##LIST;
#define DISPLAY(NAME) \
DIAGNOSE(NAME); cout << STRINGIFY(NAME) << "\t" << Contents_##NAME::print() << "\n";
/* === Test data === */
@ -355,7 +287,7 @@ namespace lumiera {
typedef Filter<ListAllConfigs::List,Instantiation<Maybe>::Test> Possible_Configs;
DISPLAY (Possible_Configs);
typedef engine::ConfigSelector<TestFactory, long, uint> TestFactorySelector;
typedef engine::config::ConfigSelector<TestFactory, long, uint> TestFactorySelector;
const long offset = 1000; // parameter fed to all TestFactory ctors
TestFactorySelector testConfigSelector (Possible_Configs::List(), offset);

View file

@ -0,0 +1,140 @@
/*
TYPELISTDIAGNOSTICS - helper for testing the typelist based utilities
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 typelistdiagnostics.hpp
** a Printer template usable for debugging the structure of a typelist built
** upon some simple debugging-style types. Examples being a Num<int> template,
** or the Flag type. A Printer type generated from this template provides
** a static \c print() function returing a string visualizing the structure
** of the typelist provided as parameter to the Printer template.
**
** @see typelistmaniptest.cpp
** @see configflagstest.cpp
**
*/
#ifndef META_TYPELISTDIAGNOSTICS_H
#define META_TYPELISTDIAGNOSTICS_H
#include "common/meta/generator.hpp"
//#include "common/meta/typelistutil.hpp"
//#include "common/meta/util.hpp"
//#include "common/util.hpp"
#include <boost/format.hpp>
using std::string;
using boost::format;
namespace lumiera {
namespace typelist {
namespace test {
namespace { // internal definitions
format fmt ("-<%u>%s");
struct NullP
{
static string print () { return "-"; }
};
/** debugging template,
* printing the "number" used for intantiation on ctor call
*/
template<class NUM=NullType, class BASE=NullP>
struct Printer;
template<class BASE>
struct Printer<NullType, BASE>
: BASE
{
static string print () { return str( fmt % "·" % BASE::print()); }
};
template<class BASE, char Fl>
struct Printer<Flag<Fl>, BASE>
: BASE
{
static string print () { return str( fmt % uint(Fl) % BASE::print()); }
};
/** call the debug-print for a typelist
* utilizing the Printer template */
template<class L>
string
printSublist ()
{
typedef InstantiateChained<L, Printer, NullP> SubList;
return SubList::print();
}
/** Spezialisation for debug-printing of a nested sublist */
template<class TY, class TYPES, class BASE>
struct Printer<Node<TY,TYPES>, BASE>
: BASE
{
static string print ()
{
typedef Node<TY,TYPES> List;
return string("\n\t+--") + printSublist<List>()+"+"
+ BASE::print();
}
};
template<char f1, char f2, char f3, char f4, char f5, class BASE>
struct Printer<Config<f1,f2,f3,f4,f5>, BASE>
: BASE
{
static string print ()
{
typedef typename Config<f1,f2,f3,f4,f5>::Flags FlagList;
return string("\n\t+-Conf-[") + printSublist<FlagList>()+"]"
+ BASE::print();
}
};
#define DIAGNOSE(LIST) \
typedef InstantiateChained<LIST::List, Printer, NullP> Contents_##LIST;
#define DISPLAY(NAME) \
DIAGNOSE(NAME); cout << STRINGIFY(NAME) << "\t" << Contents_##NAME::print() << "\n";
} // (End) internal defs
} // namespace test
} // namespace typelist
} // namespace lumiera
#endif