Typelists

Typelists是一个用来操作一大群型别的C++工具。就像list对数值提供的各种基本操作一样,typelists对型别也提供相同的操作。

有些设计模式具体制定并操作一群型别,其中也许有继承关系,显著的例子是abstract factory和visitor。如果以传统编程技术来操作一大群型别,将式全然重复的工作,如此重复将会导致隐微的代码膨胀。多数人不会想到其实它可以比现在更好。Typelists带给你一种能力,可以讲将经常性的宏工作自动化。Typelists将来自外星球的巨大威力带到C++中,让它得以支持新而有趣的一些手法。

 

3.1 Typelists的必要性

有时候你必须针对某些型别重复撰写相同代码,而templates无法帮上忙。假如有一个abstract factory,像这样:

class WidgetFactory
{
public:
     virtual Window* CreateWindow() = 0;
     virtual Button* CreateButton() = 0;
     virtual ScrollBar* CreateScrollBar() = 0;
};

 如果想将abstract factory概念泛化,不止生成window、bottom、scrollbar,而是任意型别。怎么办?

 如果你不试图泛化基本概念,就不太会有机会泛化这些概念的具体实体,虽然抽象基类很简单,但是你会陷入无穷无尽的派生类生成器中。

我们希望能够这样去使用一个abstract factory:

template<class T>
T* MakedWidget(WidgetFactory& factory)
{
     T* pW=factory.create<T>();
     pW=SetColor(RED);
     return pW;
}

 实际这是不可能的,如果Create不是虚函数,那么你就陷入了长长的Switch...case...之中,如果它是虚函数,那么虚函数不能是模板。

Typelists将使Abstract Factories泛化成真,并带来更多其它利益。

 

定义Typelists

template< class T,class U>
struct Typelist
{
    typedef T head;
    typedef T Tail;
};

  Typelists 内部没有任何数值:他的实体是空的。它们存在的理由只是为了携带型别信息。因此,对Typelists的任何处理都发生在编译期。此后我们提到一个typelist,指的是一个型别而非一个对象。虽然typlist只有两个参数,但我们可以把任意一个替换为另一个typelist,来达到无限延伸的目的。

另外我们需要一个0型别以及一个型别的Typelists,对于0型别,前面提到的NullType就可以,对于一个型别,我们可以这样定义:

typedef Typelist<int ,NullType> oneTypeOnly;

  

将Typelist生成线性化

像这样一个Typelist

typedef Typlist<int, Typelist<int, Typelist<int, Typelist<int, int> > > > Int4Type;

它太Lisp了,我们定义一系列的宏来完成它,这样我们可以忘记尖括号间的空格。

#define TYPELIST_1(T1) Typelist<T1, NullType>
#define TYPELIST_2(T1, T2) Typelist<T1, TYPELIST_1(T2) >
#define TYPELIST_3(T1, T2, T3) Typelist<T1, TYPELIST_2(T2, T3) >
#define TYPELIST_4(T1, T2, T3, T4) Typelist<T1, TYPELIST_3(T2, T3, T4) >
 //etc
#define TYPELIST_50(/* */) //

这样前面的 Int4Type 就可以这样定义  

typedef Int4Type TYPELIST_4(int, int, int, int) Int4Type;

这只是包装手法的一个开端。我们访问Int4Type的最后一个元素还比较麻烦,需要

Int4Type::Tail::Tail::Tail;

  

计算长度

我们用下面代码来计算Typelist的长度

template<class TList>struct Length;
template<> struct Length<NullType>{
    enum{value=0};
};

template<class T,class U>
struct Length<Typelist<T,U>>
{
    enum{value=1+Length<U>::value};
};

由Length<T>::value得到的是一个编译期常数,我们可以用它来定义数组大小,

std::type_info* intsRtti[Length<SignedIntegrals>::value];

这个模板代码依靠递归来完成,第一个版本是全特化,第二个版本是偏特化。

 

间奏曲

这里有一个问题,在Length的实现中,我们能否用迭代来取代递归?答案是否定的。因为我们在编译期编程中,可以使用到的仅仅是:template、编译期整数计算、typedef。

1、template:更明确的说是模板特化,提供编译期的if叙述。

2、整数计算:提供数值计算能力,用以从性别转为数值,但数值是不可改变的。

3、typedef:可视为用来引进“具名的型别常数”,他们也是定义之后就冻结,你不能将typedef定义的符号重新定义为另一个型别。

 

这些特性决定了我们无法使用迭代,所谓迭代是持有一个迭代器,并改变它,知道某些条件吻合,由于编译期我们没有“可资变化的任何东西”,所以无法实现“迭代”。所以编译期运算只能象那些纯粹函数型语言,使用大量的递归。

 

索引式访问

索引式访问是Typelist访问线性化,像在static世界中,索引必须是编译期常数,一个带有索引的template声明如下:

template<class TList,unsigned int index> struct TypeAt;

 实现如下:

template<class Head,Class Tail>
struct TypeAt<Typelist<Head,Tail>,0>
{
    typedef Head Result;
}

template<class Head,class Tail,unsigned int>
struct TypeAt<Typelist<Head,Tail>,i>
{
    typedef typename TypeAt<Tail,i-1>::Reslut Result;
}   

  如果你试着越界访问,会编译出错。对Typelist进行索引访问,花费的时间和typelist大小有关,但这个时间全部花在编译期。

 

 

 查找Typelist

我们用index0f来查找Typelist中的一个型别,返回其位置,找不到就返回-1。

template <class TList, class T> struct IndexOf;
template <class T>
struct IndexOf<NullType, T>{
    enum{value = -1};
};

template <class Tail, class T>
struct IndexOf<Typelist<T, Tail>, T>{
    enum{value = 0;}
};

template <class Head, class Tail, class T>
struct IndexOf<Typelist<Head, Tail>, T>{
private:
    enum{temp = IndexOf<Tail, T>::vale};
public:
    enum{value = temp == -1 ? -1 : 1+temp};
};

最后一个特化版本是用了问号表达式来实现分支判断

  

追加元素至Typelist

修改typelist是不可能的,我们以by value方式传回一个新typelist。

template <class TList, class T> struct Append;
template <> struct Append<NullType, NullType>{
    typedef NullType Result;
};
template <class T> struct Append<NullType, T>{
    typedef TYPELIST_1(T) Result;
};
template <class Head, class Tail>
struct Append<NullType, Typelist<Head, Tail> >{
    typedef Typelist<Head, Tail> Result;
};
template <class Head, class Tail, class T>
struct Append<Typelist<Head, Tail>, T>{
    typedef Typelist<Head, typename Append<Tail,T>::Result> Result;
};

  

这个实现比较复杂,特别是最后一个偏特化版本,他递归具现Append,每次递归都将tail和待追加型别传递进去

 

移除typelist中的某个元素

template <class TList, class T> struct Erase;
template <class T> struct Erase<NullType, T>{
    typedef NullType Result;
};
template<class T, calss Tail>
struct Erase<Typelist<T, Tail> T>{
    typedef Tail Result;
};
template <class Head, class Tail, class T>
struct Erase<Typelist<Head, Tail>, T>{
    typedef Typelist<Head,
        typename Erase<Tail, T>::Result>
        Result;
};

  

  

 

posted @ 2014-11-10 16:43  liaotingpure  阅读(302)  评论(0编辑  收藏  举报