split off and test-cover simple typelist utils

these simple utils (length of typelist, maximum size
containment test) are mostly not used in conjunction
with the more elaborate typelist manipulatino utils.
Moreover, we lacked a dedicated unit test
This commit is contained in:
Fischlurch 2011-09-25 17:06:48 +02:00
parent 057f32e15b
commit 5350ef6dbe
9 changed files with 166 additions and 360 deletions

View file

@ -48,7 +48,7 @@
#define LUMIERA_META_TUPLE_H
#include "lib/meta/typelist.hpp"
#include "lib/meta/typelist-manip.hpp"
#include "lib/meta/typelist-util.hpp"
#include "lib/meta/typeseq-util.hpp"
#include "lib/meta/util.hpp"

View file

@ -1,5 +1,5 @@
/*
TYPELIST-UTIL.hpp - Utils for working with lists-of-types
TYPELIST-MANIP.hpp - Utils for working with lists-of-types
Copyright (C) Lumiera.org
2008, Hermann Vosseler <Ichthyostega@web.de>
@ -21,8 +21,8 @@
*/
/** @file typelist-util.hpp
** Metaprogramming: Helpers for manipulating lists-of-types.
/** @file typelist-manip.hpp
** Metaprogramming: Helpers for manipulating lists-of-types.
** Sometimes, we use metaprogramming to generate a variation of concrete
** implementations by combining some basic building blocks. Typically, there
** is a number of similar, but not suitably related types involved. We want to
@ -49,54 +49,17 @@
*/
#ifndef LUMIERA_META_TYPELIST_UTIL_H
#define LUMIERA_META_TYPELIST_UTIL_H
#ifndef LUMIERA_META_TYPELIST_MANIP_H
#define LUMIERA_META_TYPELIST_MANIP_H
#include "lib/meta/typelist.hpp"
namespace lumiera {
namespace typelist{
/**
* Metafunction counting the number of Types in the collection
* @return an embedded constant \c value holding the result
*/
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
};
};
/** pick the n-th element from a typelist */
template<class TYPES, uint i>
struct Pick
@ -111,7 +74,7 @@ namespace typelist{
template<class TY, class TYPES, uint i>
struct Pick<Node<TY,TYPES>, i>
{
typedef typename Pick<TYPES, i-1>::Type Type;
typedef typename Pick<TYPES, i-1>::Type Type;
};
@ -240,7 +203,7 @@ namespace typelist{
/**
* Allows to access various parts of a given typelist:
* Start and End, Prefix and Tail..
* Start and End, Prefix and Tail...
*/
template<class TYPES>
struct Dissect;
@ -356,7 +319,7 @@ namespace typelist{
template<class FLAGS>
struct CombineFlags
{
typedef typename Combine<FLAGS, FlagOnOff>::List List;
typedef typename Combine<FLAGS, FlagOnOff>::List List;
};

View file

@ -1,5 +1,5 @@
/*
TYPELIST-UTIL.hpp - Utils for working with lists-of-types
TYPELIST-UTIL.hpp - simple helpers for working with lists-of-types
Copyright (C) Lumiera.org
2008, Hermann Vosseler <Ichthyostega@web.de>
@ -22,28 +22,21 @@
/** @file typelist-util.hpp
** Metaprogramming: Helpers for manipulating lists-of-types.
** Sometimes, we use metaprogramming to generate a variation of concrete
** implementations by combining some basic building blocks. Typically, there
** is a number of similar, but not suitably related types involved. We want to
** process those types using a common scheme, without being forced to squeeze
** all those types into a artificial inheritance relationship. Now, generating
** some kind of common factory or adapter, while mixing in pieces of code tailored
** specifically to the individual types, allows still to build a common processing
** in such situations.
** Metaprogramming: simple helpers for working with lists-of-types.
** This header provides some very basic "meta functions" for extracting
** some informations from a list-of-types. In Lumiera, we use template
** metaprogramming and especially such lists-of-types, whenever we build
** some common implementation backbone, without being able to subsume all
** participating types (classes) into a single inheritance hierarchy.
**
** The facilities in this header provide the basics of simple functional list
** processing (mostly with tail recursion). Usually, there is one template parameter
** TYPES, which accepts a \em Type-list. The result of the processing step is then
** accessible as an embedded typedef named \c List . Here, all of the 'processing'
** to calculate this result is performed by the compiler, as a side-effect of
** figuring out the resulting concrete type. At run time, in the generated
** code, typically the resulting classes are empty, maybe just
** exposing a specifically built-up function.
** The "meta functions" defined here are templates; to access the "result" of
** such a meta function, we instantiate the template and then access one of the
** embedded constant definitions (usually the enum constant named \c value)
**
** @see generator.hpp
** @see typelist-manip-test.cpp
** @see TimeControl_test usage example
** @see TypelistUtil_test
** @see lib::SimpleAllocator usage example (for isInList)
** @see TypelistManip_test
** @see typelist.hpp
**
*/
@ -53,7 +46,7 @@
#define LUMIERA_META_TYPELIST_UTIL_H
#include "lib/meta/typelist.hpp"
namespace lumiera {
@ -77,6 +70,7 @@ namespace typelist{
enum{ value = 1 + count<TYPES>::value };
};
/**
* Metafunction " max( sizeof(T) ) for T in TYPES "
*/
@ -97,268 +91,35 @@ namespace typelist{
};
/** pick the n-th element from a typelist */
template<class TYPES, uint i>
struct Pick
{
typedef NullType Type;
};
template<class TY, class TYPES>
struct Pick<Node<TY,TYPES>, 0>
{
typedef TY Type;
};
template<class TY, class TYPES, uint i>
struct Pick<Node<TY,TYPES>, i>
{
typedef typename Pick<TYPES, i-1>::Type Type;
};
/** apply a transformation (template) to each type in the list */
template<class TY, template<class> class _TRANS_>
struct Apply { typedef TY List; };
template< class TY, class TYPES
, template<class> class _TRANS_
>
struct Apply<Node<TY,TYPES>, _TRANS_ > { typedef Node< typename _TRANS_<TY>::Type
, typename Apply<TYPES,_TRANS_>::List
> List; };
/** conditional node: skip an element based on evaluating a predicate */
template<bool, class T, class TAIL>
struct CondNode { typedef TAIL Next; };
template<class T, class TAIL>
struct CondNode<true, T, TAIL> { typedef Node<T,TAIL> Next; };
/** filter away those types which don't fulfil a predicate metafunction */
template< class TYPES
, template<class> class _P_ ///< a template providing a boolean member \c ::value
>
struct Filter;
template<template<class> class _P_>
struct Filter<NullType,_P_> { typedef NullType List; };
template< class TY, class TYPES
, template<class> class _P_
>
struct Filter<Node<TY,TYPES>,_P_> { typedef typename CondNode< _P_<TY>::value
, TY
, typename Filter<TYPES,_P_>::List
>::Next
List; };
/** append lists-of-types */
template<class TY1, class TY2>
struct Append { typedef Node<TY1, typename Append<TY2,NullType>::List> List; };
template< class TY, class TYPES
, class TAIL
>
struct Append<Node<TY,TYPES>, TAIL> { typedef Node<TY, typename Append<TYPES, TAIL>::List> List; };
template<class TY, class TYPES>
struct Append<NullType, Node<TY,TYPES> > { typedef Node<TY,TYPES> List; };
template<class TY, class TYPES>
struct Append<Node<TY,TYPES>, NullType> { typedef Node<TY,TYPES> List; };
template<class TY1>
struct Append<TY1,NullType> { typedef Node<TY1,NullType> List; };
template<class TY2>
struct Append<NullType,TY2> { typedef Node<TY2,NullType> List; };
template<>
struct Append<NullType,NullType> { typedef NullType List; };
/** access the last list element */
template<class TYPES>
struct SplitLast;
template<>
struct SplitLast<NullType> { typedef NullType Type;
typedef NullType List; };
template<class TY>
struct SplitLast<Node<TY,NullType> > { typedef TY Type;
typedef NullType List; };
template<class TY, class TYPES>
struct SplitLast<Node<TY,TYPES> > { typedef typename SplitLast<TYPES>::Type Type;
typedef typename Append< TY,
typename SplitLast<TYPES>::List
>::List
List; };
/**
* splice a typelist like an overlay
* into an base typelist, starting at given index.
* @return either the combined (spliced) List, or
* the Front/Back part before or after the Overlay
* @note using a NullType as OVERLAY allows to extract
* an arbitrary Front/Back part of the list
*/
template<class BASE, class OVERLAY, uint i=0>
struct Splice;
template<class B, class BS,
class OVERLAY, uint i>
struct Splice<Node<B,BS>, OVERLAY, i> { typedef Node<B, typename Splice<BS, OVERLAY, i-1>::List> List;
typedef Node<B, typename Splice<BS, OVERLAY, i-1>::Front> Front;
typedef typename Splice<BS, OVERLAY, i-1>::Back Back; };
template<class B, class BS,
class O, class OS >
struct Splice<Node<B,BS>,Node<O,OS>,0> { typedef Node<O, typename Splice<BS,OS, 0>::List> List;
typedef NullType Front;
typedef typename Splice<BS,OS, 0>::Back Back; };
template<class B, class BS>
struct Splice<Node<B,BS>, NullType, 0> { typedef Node<B, BS> List;
typedef NullType Front;
typedef Node<B, BS> Back; };
template<class XX, uint i>
struct Splice<NullType, XX, i> { typedef NullType List;
typedef NullType Front;
typedef NullType Back; };
/**
* Allows to access various parts of a given typelist:
* Start and End, Prefix and Tail..
* Metafunction to check if a specific type is contained
* in a given typelist. Only exact match is detected.
*/
template<class TYPES>
struct Dissect;
template<class T, class TYPES>
struct Dissect<Node<T,TYPES> >
template<typename TY, typename TYPES>
struct IsInList
{
typedef Node<T,TYPES> List; ///< the complete list
typedef T Head; ///< first element
typedef Node<T,NullType> First; ///< a list containing the first element
typedef TYPES Tail; ///< remainder of the list starting with the second elm.
typedef typename SplitLast<List>::List Prefix;///< all of the list, up to but excluding the last element
typedef typename SplitLast<List>::Type End; ///< the last element
typedef Node<End,NullType> Last; ///< a list containing the last element
enum{ value = false };
};
template<>
struct Dissect<NullType>
template<typename TY, typename TYPES>
struct IsInList<TY, Node<TY,TYPES> >
{
typedef NullType List;
typedef NullType Head;
typedef NullType First;
typedef NullType Tail;
typedef NullType Prefix;
typedef NullType End;
typedef NullType Last;
enum{ value = true };
};
/**
* prefix each of the elements,
* yielding a list-of lists-of-types
*/
template<class T, class TY>
struct PrefixAll { typedef Node< typename Append<T,TY>::List, NullType> List; };
template<class T>
struct PrefixAll<T, NullType> { typedef NullType List; };
template<class T>
struct PrefixAll<T, NodeNull> { typedef Node< typename Append<T,NodeNull>::List, NullType> List; };
template< class T
, class TY, class TYPES
>
struct PrefixAll<T, Node<TY,TYPES> > { typedef Node< typename Append<T,TY>::List
, typename PrefixAll<T,TYPES>::List
> List; };
/**
* build a list-of lists, where each element of the first arg list
* gets in turn prepended to all elements of the second arg list.
* Can be used to build all possible combinations from two
* sources, i.e. the Cartesian product.
*/
template<class TY1,class TY2>
struct Distribute { typedef typename PrefixAll<TY1,TY2>::List List; };
template<class TY>
struct Distribute<NullType,TY> { typedef NullType List; };
template< class TY, class TYPES
, class TAIL
>
struct Distribute<Node<TY,TYPES>,TAIL> { typedef typename Append< typename PrefixAll<TY,TAIL>::List
, typename Distribute<TYPES,TAIL>::List
>::List
List; };
/**
* build all possible combinations, based on a enumeration of the basic cases.
* For each of the types in the argument list, an "enumeration generator" template is invoked,
* yielding a list of the possible base cases. These base cases are then combined with all the
* combinations of the rest, yielding all ordered combinations of all cases. Here, "ordered"
* means that the base cases of the n-th element will appear in the n-th position of the
* resulting lists,
*
* For the typical example, the "base cases" are {flag(on), flag(off)}, so we get a
* list-of-lists, featuring all possibilities to combine these distinct toggles.
*/
template< class X
, template<class> class _ENUM_>
struct Combine { typedef typename Distribute< typename _ENUM_<X>::List
, Node<NullType,NullType>
>::List List; };
template< template<class> class _ENUM_>
struct Combine<NullType, _ENUM_ > { typedef NodeNull List; };
template< class TY, class TYPES
, template<class> class _ENUM_>
struct Combine<Node<TY,TYPES>,_ENUM_> { typedef typename Distribute< typename _ENUM_<TY>::List
, typename Combine<TYPES,_ENUM_>::List
>::List List; };
/** enumeration generator for the Combine metafunction,
* yielding an "on" and "off" case
*/
template<class F>
struct FlagOnOff
{
typedef Node<F, Node<NullType,NullType> > List;
};
/** generate all possible on-off combinations of the given flags */
template<class FLAGS>
struct CombineFlags
{
typedef typename Combine<FLAGS, FlagOnOff>::List List;
template<typename TY, typename XX, typename TYPES>
struct IsInList<TY, Node<XX,TYPES> >
{
enum{ value = IsInList<TY,TYPES>::value };
};
/** convenience shortcut: query function */
template<typename TY, typename TYPES>
bool
isInList()
{
return IsInList<TY,TYPES>::value;
}

View file

@ -40,15 +40,25 @@ This code is heavily inspired by
/** @file typelist.hpp
** A template metaprogramming technique for manipulating collections of types.
** Effectively this is a taylored and simplified version of what can be found in the Loki library.
** Effectively this is a tailored and simplified version of what can be found in the Loki library.
** We use it in other generic library-style code to generate repetitive code. If you tend to find
** template metaprogramming (or functional programming in general) offending, please ignore the
** technical details and just consider the benefit of such an simplification for the user code.
**
** Interface for using this facility is the template Types(.....) for up to 20 Type parameters
** technical details and just consider the benefit of such an simplification for the client code.
**
** Interface for using this facility is the template Types(.....) for up to 20 Type parameters.
** To start typelist processing, other templates typically pick up the Types<...>::List type.
** This allows for LISP-style list processing, with a pattern match on either Node<TY,TYPES>
** or NullType to terminate recursion. In C++ template metaprogramming, "pattern match"
** is done by partial template specialisations (the compiler will pick up and thus
** match the template parameters). A typedef acts like a declaration in normal
** programming. Because such a "declaration" can't be changed after the fact,
** effectively this is a flavour of functional programming. Just the
** "execution environment" is the compiler, during compilation.
**
** @see lumiera::visitor::Applicable usage example
** @see typelisttest.cpp
** @see control::CommandSignature more elaborate usage example (dissecting a functor signature)
** @see TypeList_test
** @see TypeListManip_test
**
*/

View file

@ -49,6 +49,7 @@
//#include "pre.hpp"
#include "lib/error.hpp"
#include "lib/meta/generator.hpp"
#include "lib/meta/typelist-util.hpp"
#include "lib/format.hpp"
//#include "lib/typed-counter.hpp"
#include "include/logging.h"
@ -63,31 +64,11 @@
namespace lib {
// using std::tr1::shared_ptr;
using lumiera::typelist::InstantiateForEach;
using lumiera::typelist::Types;
using lumiera::typelist::IsInList;
using lumiera::typelist::InstantiateForEach;
namespace {
using lumiera::typelist::Node;
using lumiera::typelist::NullType;
template<typename TYPES, typename TY>
struct IsInList
{
enum{ value = false };
};
template<typename TY, typename TYPES>
struct IsInList<Node<TY,TYPES>, TY>
{
enum{ value = true };
};
template<typename XX, typename TYPES, typename TY>
struct IsInList<Node<XX,TYPES>, TY>
{
enum{ value = IsInList<TYPES,TY>::value };
};
}
@ -141,9 +122,9 @@ namespace lib {
___assertSupportedType()
{
typedef typename TYPES::List PreconfiguredTypes;
typedef IsInList<PreconfiguredTypes,XX> IsPreconfiguredType;
typedef IsInList<XX, PreconfiguredTypes> IsSupportedType;
BOOST_STATIC_ASSERT (IsPreconfiguredType::value);
BOOST_STATIC_ASSERT (IsSupportedType::value);
}

View file

@ -45,7 +45,7 @@
#define LIB_VARIANT_H
#include "lib/meta/typelist-manip.hpp"
#include "lib/meta/typelist-util.hpp"
#include "lib/meta/generator.hpp"
#include <boost/noncopyable.hpp>

View file

@ -30,7 +30,7 @@
** we need to accept functor objects with a very specific and predetermined
** signature, thus allowing for strict type checking by the compiler.
**
** \par Relation of function signatures
** \par Relation of function signatures (MEM = type of the "memento" for Undo)
** - operation: void(P1,..PN)
** - captureUndo: MEM(P1,..PN)
** - undoOperation void(P1,..PN,MEM)
@ -59,24 +59,20 @@
namespace control {
// using lib::Symbol;
// using std::tr1::shared_ptr;
using std::tr1::function;
using lumiera::typelist::FunctionSignature;
using lumiera::typelist::FunctionTypedef;
using lumiera::typelist::Types;
//using lumiera::typelist::NullType;
//using lumiera::typelist::Tuple;
using lumiera::typelist::Append;
using lumiera::typelist::SplitLast;
/**
* Metaprogramming helper for building Command function signatures.
* Metaprogramming helper for building Command function signatures.
* The complete definition context of any command is templated to the signature
* of the actual command operation and to the memento type. The typedefs embedded
* within CommandSignature<SIG,MEM> allows accepting suitable typed functions
* within CommandSignature<SIG,MEM> allows for accepting suitable typed functions
* to implement the command in question.
*/
template<typename SIG, typename MEM>
@ -90,7 +86,7 @@ namespace control {
public:
typedef typename FunctionTypedef<void, Args>::Sig OperateSig;
typedef typename FunctionTypedef<MEM, Args>::Sig CaptureSig;
typedef typename FunctionTypedef<MEM, Args>::Sig CaptureSig;
typedef typename FunctionTypedef<void, ExtendedArgs>::Sig UndoOp_Sig;
typedef Args CmdArgs;
typedef MEM Memento;
@ -100,16 +96,23 @@ namespace control {
/**
* Type analysis helper template.
* Type analysis helper template.
* Used for dissecting a given type signature to derive
* the related basic operation signature, the signature of a possible Undo-function
* and the signature necessary for capturing undo information. The implementation
* relies on re-binding an embedded type defining template, based on the actual
* case, as identified by the structure of the given parameter signature.
*
* To use this template, it is instantiated with the signature of a functor object
* in question. Depending on the actual situation, the compiler will then either
* pick Case1 or Case2 -- thus allowing the client in any case to pick up the
* correct signatures for Operation, Capture and Undo-function from the
* public typedefs within \c UndoSignature
*/
template<typename SIG>
class UndoSignature
{
// preparation: dissect the function signature into arguments and result
typedef typename FunctionSignature< function<SIG> >::Args Args;
typedef typename FunctionSignature< function<SIG> >::Ret Ret;

View file

@ -801,6 +801,11 @@ return: 0
END
TEST "simple typelist utils" TypeListUtil_test <<END
return: 0
END
TEST "TypeListGenerator_test" TypeListGenerator_test <<END
out: ctor DoIt<Block<13> >
out: ctor DoIt<Block< 8> >

View file

@ -0,0 +1,83 @@
/*
TypeListUtil(Test) - check the simple typelist metaprogramming helpers
Copyright (C) Lumiera.org
2011, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
#include "lib/test/run.hpp"
#include "lib/meta/typelist-util.hpp"
namespace lumiera {
namespace typelist{
namespace test {
typedef Types< int
, uint
, int64_t
, uint64_t
>::List TheList;
typedef Types< >::List EmptyList;
/*************************************************************************
* @test verify the simple helpers for working with lists-of-types.
* These are simple metafunctions to count the number of elements,
* calculate the maximum size or check for inclusion.
*
* Because these metafunctions will be computed during compilation,
* mostly the function is already verified when it passes compilation.
* All we can do here, at runtime, is to verify some of the (expected)
* constant results.
*/
class TypeListUtil_test : public Test
{
void
run (Arg)
{
CHECK (4 == count<TheList>::value);
CHECK (0 == count<EmptyList>::value);
CHECK (sizeof(int64_t) == maxSize<TheList>::value);
CHECK (0 == maxSize<EmptyList>::value);
CHECK ( bool(IsInList<int , TheList>::value));
CHECK ( bool(IsInList<uint , TheList>::value));
CHECK ( bool(IsInList<int64_t , TheList>::value));
CHECK ( bool(IsInList<uint64_t, TheList>::value));
CHECK (!bool(IsInList<double , TheList>::value));
CHECK (!bool(IsInList<int , EmptyList>::value));
CHECK (!bool(IsInList<int , int>::value)); // Note: not-a-typelist yields false too
}
};
/** Register this test class... */
LAUNCHER (TypeListUtil_test, "unit common");
}}} // namespace lumiera::typelist::test