C++自動化(模板元)編程基礎與應用

C++自動化(模板元)編程基礎與應用

 

作者:Ackarlix

挨踢網【中文IT技術社區】http://www.aitic.net

 

 

#if 0

   
大家好,在這一類的文章中將會系統的介紹模板元編程的相關基礎知識。最主要的是
這類文章的目的就是讓不熟悉模板元的C++迷們儘快的進入到模板元的奇妙世界裏面來,所
以每一篇文章都將只會討論一個話題,儘可能的把這個話題說清楚明白就可以了。

   
好了,言歸正傳。大家都知道C++是一們計算機語言,這一點也沒錯,但是你知道C++
裏面還包含了另外一種子語言麼?呵呵,恐怕知道的人就不多了,會用的人就更少了。但
是既然經過了這麼多年的發展,C++語言裏面出現了這種子語言(模板)自然有它的根源的
,這一點並不是本文中將要討論的,如果想了解C++的發展例程,可以參見相關的文獻。

   
在本文中將要說明的問題是:爲什麼說C++模板是一種語言呢?

   
爲了回答這個問題,首先需要考慮一下什麼是計算機語言,關於這個精確的定義,很
多的計算機基礎教程上都有,在這裏我給出一種比較窄的定義:

   
能夠在計算機上表達選擇結構,循環結構,同時能夠進行進行整數的四則運算的體系
就是一種計算機語言。

   
很顯然,C++自然是一種計算機語言了,還有Basic,Fortran,Pascal等等都是計算機語
言。之所以討論這麼多的概念問題是爲了說明:如何證明C++的模板語法是一種計算機語言
。又因爲模板是C++語言的一個元素,所以又可以將C++模板語法稱爲C++的二級語言或者子
語言。在本文中將會通過使用模板分別實現整數四則運算,選擇結構以及循環結構來證明
C++
模板語法構成了一個完整的計算機語言。

   
另外特別值得注意的是,因爲C++的模板語言是在編譯器編譯的時候完成的,所以又稱
爲靜態語言,通常的C++語言又稱爲動態語言或者運行時語言。正是因爲模板語言是在編譯
期完成的,所以我們可以藉助於這種編譯期的計算實現代碼自動生成的目的,從而實現C++
自動化編程。這是後續的文章中會詳細討論的。

   
首先看看,模板是如何完成編譯期四則計算的。

#endif

#ifdef CODE1//
編譯期四則計算的示例代碼
#include <iostream>
template<size_t i,size_t j> struct Add { enum
{value = i+j}; };
template<size_t i,size_t j> struct Sub { enum
{value = i-j}; };
template<size_t i,size_t j> struct Mul { enum
{value = i*j}; };
template<size_t i,size_t j> struct Div { enum
{value = i/j}; };
int
main()
{
        std::cout <<
"4+2=" << Add<4,2
>::value << std::endl;
        std::cout <<
"4-2=" << Sub<4,2
>::value << std::endl;
        std::cout <<
"4*2=" << Mul<4,2
>::value << std::endl;
        std::cout <<
"4/2=" << Div<4,2
>::value << std::endl;
       
//
爲了證明上面的計算是在編譯期進行的,我們編寫下面的代碼測試
        //
將模板值作爲數組定義時使用的參數就可以證明是在編譯期執行的計算:)
        int a[Add<4,2>::value];//
這麼定義並沒有錯
        int b[Sub<4,2>::value];//
這麼定義並沒有錯
        int c[Mul<4,2>::value];//
這麼定義並沒有錯
        int d[Div<4,2>::value];//
這麼定義並沒有錯
        std::cout << sizeof(a)/sizeof(int) << std::endl;
        std::cout <<
sizeof(b)/sizeof(int
) << std::endl;
        std::cout <<
sizeof(c)/sizeof(int
) << std::endl;
        std::cout <<
sizeof(d)/sizeof(int
) << std::endl;
       
return 0
;
}
#endif//CODE1
////////////////////////////////////////////////////////////////////////////////
//
程序運行結果如下所示:
/*******************************************************************************
4+2=6
4-2=2
4*2=8
4/2=2
6
2
8
2
*******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
#if 0

   
從代碼CODE1中可以看出使用整型模板參數的模板是可以實現編譯期計算的,在這裏,
證明了這個計算過程是在編譯期完成的。

   
好了,現在看看如何使用C++模板實現選擇結構,見代碼CODE2

#endif
#ifdef CODE2//
編譯期實現選擇的示例代碼
#include <iostream>
template <bool Condition,class Then,class
Else>
struct
IF
{
       
typedef Then result;//
Then類型作爲條件爲真的返回值(返回值爲類型)
};
template<class Then,class
Else>
struct IF<false
,Then,Else>
{
       
typedef Else result;//
Else類型作爲條件爲假的返回值(返回值爲類型)
};
//
爲了測試這個IF選擇結構,需要下面的兩個類型定義:
struct True {static void Print(){std::cout << "
" << std::endl;}};
struct False{static void Print(){std::cout << "
"
<< std::endl;}};
int
main()
{
        IF<
1==1
,True,False>::result::Print();
        IF<
1!=1
,True,False>::result::Print();
       
return 0
;
}
#endif//CODE2
////////////////////////////////////////////////////////////////////////////////
//
程序運行結果如下所示:
/*******************************************************************************


*******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
#if 0

   
CODE2中可以看出,這裏操作的對象是類型,而CODE1中的操作對象是整數,到了這
裏可以總結如下:C++模板元編程中的操作對象只有兩種,一種是整形數,包括boolchar
intlong(signed unsigned)都可以,它們都可以當作整數使用,賦值和保存結果的
方式都是通過枚舉變量來實現;另一種就是類型了,賦值和保存結果都是通過typedef來實
現的。例如CODE2中將IF的選擇結果以Result的方式保存作爲結果就是通過typedef實現的。

   
再來看看循環結構:

#endif

#ifdef CODE3//
編譯期實現循環的示例代碼
#include <iostream>
//
爲了簡單採用一個階乘作爲例子,因爲如果用普通的C++語法來實現階乘函數的話需要
//
一個循環結構的,這裏採用模板遞歸的方式實現了這種階乘,也就實現了一種特殊的
//
循環結構。
template<size_t n> struct Power {
       
enum{value=n*Power<n-1>::value}; //
循環遞歸過程
};
template<> struct Power<0
> {
       
enum{value=1}; //0
的階乘是1,也是循環的終止條件
};
int
main()
{
       
int a[Power<5>::value];//
同樣用數組參數來判斷是否在編譯期完成計算
        std::cout << sizeof(a)/sizeof(int) << std::endl;
       
return 0
;
}
#endif//CODE3
////////////////////////////////////////////////////////////////////////////////
//
程序運行結果如下所示:
/*******************************************************************************
120
*******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
#if 0

   
CODE3中我們可以看出Power是通過模板遞歸的方式實現循環的,而且這個循環過程
是在編譯期完成的。到了這裏可以總結出:C++模板元編程中實現循環的方式只有一種,那
就是模板遞歸實現循環。雖然這裏的Power的循環不怎麼直接,但是它確確實實是一個循環
結構,只不過是一個非常特殊的循環結構。實際上採用模板遞歸的方法可以實現普通C++
法裏面的for循環,while循環,do-while循環這些通用的循環結構。

   
到目前爲止,已經成功的證明了C++模板是一個完整的計算機語言。既然是一門語言,
當然可以做許許多多的事情,這就在於每個人的發揮了。好了,在本文的最後給出一個通
用的LOOP循環作爲本文的結束,這個LOOP循環可以進行簡單的循環算法設計了,下面的例
子中將會說明這一點:

#endif
#ifdef CODE4
#include <iostream>
template<size_t n> void
print()
{
//
這裏的n是編譯期的結果,可以用來定義數組的
        int a[n+1];//
這麼做是爲了證明n是編譯期常量,同時避免出現零個元素的數組
        std::cout << sizeof(a)/sizeof(int)-1 << " " ;
}
template <size_t i>struct
LOOP
{
       
static void execute(){LOOP<i-1
>::execute();print<i>();}
};
template <>struct LOOP<0>//
循環終止條件
{
       
static void execute(){print<0
>();}
};
int
main()
{
        LOOP<
5
>::execute();
       
return 0
;
}
#endif//CODE4
////////////////////////////////////////////////////////////////////////////////
//
程序運行結果如下所示:
/*******************************************************************************
0 1 2 3 4 5
*******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
#if 0

    
CODE4中可以看出,這個靜態LOOP循環是一個相對來說通用的循環代碼,只需要將自
己的功能代碼寫入到一個函數(print)中就可以實現靜態循環了,更重要的是,這個靜態
LOOP
循環實現了靜態代碼和動態代碼的連接,因此用途更加廣泛,主要可以用來產生代碼
。關於如何產生代碼,以及如何使用將是本類文章的後續文章討論的內容。

   
本章完。
   
   
在下一章裏面將會詳細的討論這裏的LOOP循環並且一步一步的進行演化將這個LOOP
環寫得越來越通用,這裏面是一個學習過程,也是理解這裏的靜態循環的一個理解過程。
(敬請關注!)

   
未完,待續...
   
#endif

#if 0

   
在上一篇文章的最後提到了一個相對來說通用一點的LOOP循環,下面還是將上一篇文
章中的LOOP循環代碼複製如下:

#endif
#ifdef CODE1
#include <iostream>
template<size_t n> void
print()
{
//
這裏的n是編譯期的結果,可以用來定義數組的
        int a[n+1];//
這麼做是爲了證明n是編譯期常量
        std::cout << sizeof(a)/sizeof(int)-1 << " " ;
}
template <size_t i>struct
LOOP
{
       
static void execute(){LOOP<i-1
>::execute();print<i>();}
};
template <>struct LOOP<0>//
循環終止條件
{
       
static void execute(){print<0
>();}
};
int
main()
{
        LOOP<
5
>::execute();
       
return 0
;
}
#endif//CODE1
////////////////////////////////////////////////////////////////////////////////
//
程序運行結果如下所示:
/*******************************************************************************
0 1 2 3 4 5
*******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
#if 0

   
現在所需要考慮的問題是怎麼將上面的print模板函數書寫得更通用些,因爲每定義一個
新的print函數就需要重新書寫兩個LOOP模板,爲了避免這種重複代碼得書寫,在此很容易想
到的是將這個print模板函數作爲模板參數傳遞,好了讓我們開始新的嘗試吧。具體得示例代
碼如下所示:

#endif
#ifdef CODE2
#include <iostream>
//
下面的兩個模板就比CODE1裏面的LOOP模板通用多了
template <template<size_t>class Function,size_t i> struct LOOP
{
       
static void
execute()
        {
                LOOP<Function,i-
1
>::execute();
                Function<i>::execute();
        }
};
template <template<size_t>class Function> struct LOOP<Function,0
>
{
       
static void
execute()
        {
                Function<
0
>::execute();
        }
};
//
爲了模板化必須將原來的輸出函數做成一個模板結構體
template<size_t n> struct Function
{
       
static void
execute()
        {
//
這裏的n是編譯期的結果,可以用來定義數組的
                int a[n+1];//
這麼做是爲了證明n是編譯期常量
                std::cout << sizeof(a)/sizeof(int)-1 << " " ;
        }
};
//
下面是測試代碼
int main()
{
        LOOP<Function,
5
>::execute();
       
return 0
;
}
#endif//CODE2
////////////////////////////////////////////////////////////////////////////////
//
程序運行結果如下所示:
/*******************************************************************************
0 1 2 3 4 5
*******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
#if 0

   
CODE2的代碼中可以看出,CODE2中的LOOP循環將CODE1中的print模板函數模板參數
化之後就使得CODE2中的LOOP更通用一些了,在使用的時候僅僅只是需要按照Function的規
範寫一個模板結構就可以了,不必再書寫重複結構的LOOP了,從而精簡了使用時需要編寫
的代碼量。但是我們注意到結果並不複合STL的前閉後開的習慣,我們也不能很容易的理解
測試代碼main函數中的LOOP<Function,n>的意義了,爲了避免使用中的錯誤必須規定好它
的明確的意義:

    (1)
0n循環執行Function,並且是前閉後開的習慣

    (2)
Function執行n次,每次的整型參數從0遞增1

   
爲了使CODE2中的LOOP循環表達的是這兩個意義,必須對CODE2中的LOOP循環進行改寫
改寫候的代碼如下所示:
   
#endif
#ifdef CODE3
#include <iostream>
//
下面的兩個模板就比CODE1裏面的LOOP模板通用多了
template <template<size_t>class Function,size_t i> struct LOOP
{
       
static void
execute()
        {
                LOOP<Function,i-
1
>::execute();
                Function<i-
1
>::execute();
        }
};
template <template<size_t>class Function> struct LOOP<Function,0
>
{
       
static void
execute(){}
};
//
爲了模板化必須將原來的輸出函數做成一個模板結構體
template<size_t n> struct Function
{
       
static void
execute()
        {
//
這裏的n是編譯期的結果,可以用來定義數組的
                int a[n+1];//
這麼做是爲了證明n是編譯期常量
                std::cout << sizeof(a)/sizeof(int)-1 << " " ;
        }
};
//
下面是測試代碼
int main()
{
       
//
下面的這行代碼可以按照上面的兩種意義直觀的理解爲:
        //(1)
05循環執行Function,範圍[0,5)
        //(2)
Function執行5次,每次的整型參數從0遞增1
        LOOP<Function,5>::execute();
       
return 0
;
}
#endif//CODE3
////////////////////////////////////////////////////////////////////////////////
//
程序運行結果如下所示:
/*******************************************************************************
0 1 2 3 4
*******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
#if 0

   
CODE3的運行結果來看確實滿足了我們所需要表達的明確的意義,因此代碼理解起來
就非常直觀了。

   
CODE3的代碼中我們還是發現LOOP的起始參數固定爲0,這一點實際上也是可以進一
步改進的。下面我們就來看看如何使得起始模板參數也可以指定的,見代碼CODE4

#endif
#ifdef CODE4
#include <iostream>
//
加入一個外覆層來傳遞額外的模板參數
template <template<size_t>class Function,size_t start,size_t finish>
struct
LOOP
{
       
static void
execute()
        {
                LOOP_BODY<finish,
true
>::execute();
        }
private
:
       
//
引入了一個布爾型的模板參數用來確定循環的終止條件
        template <size_t i,bool> struct LOOP_BODY
        {
               
static void
execute()
                {
                        LOOP_BODY<i-
1,(i-1
>start)>::execute();
                        Function<i-
1
>::execute();
                }
        };
       
//
循環的終止語句,停止遞歸以結束循環
        template <size_t i> struct LOOP_BODY<i,false>
        {
               
static void
execute(){}
        };
};
//
爲了模板化必須將原來的輸出函數做成一個模板結構體
template<size_t n> struct Function
{
       
static void
execute()
        {
//
這裏的n是編譯期的結果,可以用來定義數組的
                int a[n+1];//
這麼做是爲了證明n是編譯期常量
                std::cout << sizeof(a)/sizeof(int)-1 << " " ;
        }
};
//
下面是測試代碼
int main()
{
       
//
下面的這行代碼可以直觀的理解爲:
        //(1)
15循環執行Function,範圍[1,5)
        LOOP<Function,1,5>::execute();
       
//
將兩個輸出分成兩行輸出
        std::cout << std::endl;
       
//(1)
25循環執行Function,範圍[2,5)
        LOOP<Function,2,5>::execute();
       
return 0
;
}
#endif//CODE4
////////////////////////////////////////////////////////////////////////////////
//
程序運行結果如下所示:
/*******************************************************************************
1 2 3 4
2 3 4
*******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
#if 0

   
CODE4中的代碼我們成功的通過添加了一層外敷類實現了循環的初始值設置,但是我們
還是應當注意CODE4中的循環步長並不能夠指定,所以在這裏我們通過類似的方法來實現步長
的指定。詳細代碼見CODE5所示:

#endif
#ifdef CODE5
#include <iostream>
//
加入一個外覆層來傳遞額外的模板參數
template <template<size_t>class Function,size_t start,size_t finish,size_t step>
struct
LOOP
{
       
static void
execute()
        {
               
//
爲了能夠正確的計算出實際的循環終止變量,需要對給定的終止變量
                //
進行計算,以滿足正確的循環語義
                LOOP_BODY<(finish/step*step+start),true>::execute();
        }
private
:
       
//
引入了一個布爾型的模板參數用來確定循環的終止條件
        template <size_t i,bool> struct LOOP_BODY
        {
               
static void
execute()
                {
                        LOOP_BODY<i-step,(i-step>start)>::execute();
                        Function<i-step>::execute();
                }
        };
       
//
循環的終止語句,停止遞歸以結束循環
        template <size_t i> struct LOOP_BODY<i,false>
        {
               
static void
execute(){}
        };
};
//
爲了模板化必須將原來的輸出函數做成一個模板結構體
template<size_t n> struct Function
{
       
static void
execute()
        {
//
這裏的n是編譯期的結果,可以用來定義數組的
                int a[n+1];//
這麼做是爲了證明n是編譯期常量
                std::cout << sizeof(a)/sizeof(int)-1 << " " ;
        }
};
//
下面是測試代碼
int main()
{
       
//
下面的這行代碼可以直觀的理解爲:
        //(1)
110循環執行Function,範圍[1,10),步長2
        LOOP<Function,1,10,2>::execute();
       
//
將兩個輸出分成兩行輸出
        std::cout << std::endl;
       
//(1)
210循環執行Function,範圍[2,10),步長3
        LOOP<Function,2,10,3>::execute();
       
//
暫時還不能允許下面的逆向調用方式
        //LOOP<Function,10,2,-3>::execute();
        return 0
;
}
#endif//CODE5
////////////////////////////////////////////////////////////////////////////////
//
程序運行結果如下所示:
/*******************************************************************************
1 3 5 7 9
2 5 8
*******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
#if 0

   
CODE5的代碼中可以看出明顯存在一個缺陷:並不能夠實現逆向的循環操作。這在類
型操作中並不是什麼缺陷!因爲在後續的章節的代碼產生機制中並不需要逆向的循環操作
,所以爲了使得本文儘量的簡單,在這裏並不討論這個問題,這個問題留給讀者思考,如
果在本類文章的後續章節中需要這個機制,那麼我會在需要的地方實現之。

   
CODE5的運行結果來看,CODE5的實現已經滿足了我們日常編碼的需要了,實際上用
這裏介紹的方法還可以非常容易的實現許許多多的循環語句,包括FOR循環,WHILE循環等
等,但是需要注意的是:採用模板元編程主要是用來實現自己的功能代碼的,使用這裏介
紹的循環代碼已經可以滿足後面進行代碼生成的需要了。所以本文的靜態循環代碼介紹也
可以到此打住了,不過各位有興趣的話,可以和我聯繫,我非常願意和您討論這些話題:)

   
本章完。

   
在下一章裏將會介紹代碼的生成的機制,並通過解決一些簡單的問題來說明代碼生成
爲什麼可以實現自動化過程。(敬請關注!)

   
未完,待續...

#endif

#if 0

   
在前面的兩章裏面討論了C++模板元作爲C++的一門二級語言的問題,並給出了常用的
程序設計語言的語素的實現,是一個完備的體系。總的來說,前面的章節裏面是採用了下
面的方法來實現這些語素的:

    (1)
整數計算結果通過enum變量進行保存

    (2)
類型計算結果通過typedef進行保存

    (3)?:
運算符可用來實現靜態整型表達式的選擇功能

    (4)
模板特化可用來實現靜態類型表達式的選擇功能

    (5)
模板遞歸可用來實現靜態循環,循環變化元素只能夠是整數

    (6)
通過整數可以映射到類型,所以循環變化元素也可以間接爲類型

   
這一章裏面我們將要討論另外的問題,所採用的方法也是這些方法。那麼本文將要討
論的問題是:

   
如何實現類型循環,也就是上面總結出來的第(6)種技巧。

   
關於這一點的討論,我認真參考了<<Modern C++ Design>>一書的Typelist,在本文中
將會以cons來表達類型列表的概念,並對<<Modern C++ Design>>一書的Typelist相關的操
作進行精簡,得到我們將會在生成代碼的過程中使用的模板元函數。不用的根本不會考慮
,所以爲了使撤銷和重做庫儘可能的獨立些,所以我不採用Loki庫,這樣使得該撤銷和重
做庫的安裝比較簡單。

   
爲此,首先實現一個類型串類型名叫cons,代碼如下:
   
#endif

#ifdef CODE_NOTE
//cons
的實現,採用和STL類似的類型命名方式
template <class FirstType,class SecondType>
struct
cons
{
       
typedef
FirstType  first_type;
       
typedef
SecondType second_type;
};
//
有了上面的cons的實現,現在就可以很容易的將類型放入到這個串中了:
typedef cons<char,
        cons<
int
,
        cons<
short
,
        cons<
long
,
        cons<
float
,
       
double
> > > > > CONS;
//
上面的代碼定義了一個6中基本類型作爲元素的類型串CONS,那麼該怎麼實現對上面的類
//
型的操作呢?仔細想想C語言裏面的字符串是如何處理字符串的終結的啊!呵呵可能你已
//
經想到了,需要一個特殊的終結符,對了,爲了能夠操作上面的類型容器我們需要定義
//
一個終結符類型:
struct null_type;
//
有了上面的null_type,現在的類型容器CONS重新表述如下:
typedef cons<char,
        cons<
int
,
        cons<
short
,
        cons<
long
,
        cons<
float
,
        cons<
double
,
        null_type> > > > > > CONS;
#endif//CODE_NOTE

#if 0

   
有了上面的cons類型和終結符已經可以用來表達類型串的概念了,既然有了類型串,
那就應該有類型串的相關操作,這裏我們首先考慮的是,這些操作將會應用到什麼地方?
考慮一下如何使用前面的LOOP靜態循環來遍歷cons串中的所有類型呢?很顯然通過本文開
頭對前文的總結(5)我們知道靜態LOOP循環可以變化的只能是整數,那麼很自然的爲了能夠
實現類型遍歷必須實現整數和類型的映射和反映射元函數。一種直接的方式就是根據索引
值得到cons串中的每一個類型,這樣就需要至少兩個元函數來完成這種循環,這兩個元函
數分別是:

    (a)length
元函數,用來獲得cons串的長度
   
    (b)type
元函數,用來根據索引值得到相應的cons串在該位置的類型

   
有了上面的兩個元函數就可以很方便的使用LOOP靜態循環來實現類型遍歷的過程了。
現面來看看如何實現這兩個元函數:在給出這兩個元函數的實現之前,爲了明確起見,需
要準確定義這兩個元函數的返回值的意義

    (length)
根據具體的CONS類型得到的是cons串的長度,這個長度指的是不包括類型串
結束符的元素個數,這一點和C字符串的strlen庫函數的行爲類似。

    (type)
根據具體的CONS類型和一個給定的索引值得到一個類型,指的是cons類型串中
的指定索引值的位置的類型,這個索引值符合C語言的規範,是從0開始編號的。

   
在給出具體的這兩個元函數的實現之前需要將前面的章節中的命名規範說明一下:
   
    (A)
如果元函數的返回值爲整數,那麼返回值名稱命名爲value

    (B)
如果元函數的返回值爲類型,那麼返回值名稱命名爲result
   
   
好了,現在可以看看如何實現這兩個元函數了:

#endif

#ifdef CODE_NOTE
//length
元函數的實現
template<class Type>struct length;
template<>struct
length<null_type>
{
//
返回值爲整數,命名爲value
        enum{value=0};
};
template<class FirstType,class
SecondType>
struct
length<cons<FirstType,SecondType> >
{
//
返回值爲整數,命名爲value
        enum{value=1+length<SecondType>::value};
};
//type
元函數的實現
template<class Cons,size_t index>struct type;
template<class FirstType,class
SecondType>
struct type<cons<FirstType,SecondType>,0
>
{
//
返回值爲類型,命名爲result
        typedef FirstType result;
};
template<class FirstType,class SecondType,size_t
i>
struct
type<cons<FirstType,SecondType>,i>
{
//
返回值爲類型,命名爲result
        typedef typename type<SecondType,i-1>::result result;
};
#endif//CODE_NOTE

#if 0

   
兩個需要的元函數已經成功的給出了,如果有什麼不清楚的地方也可以參見<<Modern
C++ Design>>
一書的TypelistLength元函數和TypeAt元函數的解釋,基本上是一樣的,
這裏不在進行過多的解釋。在這裏我們所重視的是如何使用這兩個元函數來實現我們的類
型遍歷算法。現咱萬事具備了,可以開始本章的中心內容:實現類型的遍歷算法!不過在
給出具體的代碼之前還需要做一件事情,那就是將之前的代碼保存到一個文件中,爲了統
一起見,我們就先將這個文件名命名爲meta.h吧!該文件的詳細內容在本文的最後給出。
現在看一下如何實現我們的類型遍歷算法吧,見CODE1所示:

#endif

#ifdef CODE1
#include <iostream>
#include "meta.h"//
該文件的具體內容見本文的最後
namespace xcl = pandaxcl;//
縮寫名字空間
//
爲了能夠測試type元函數需要定義下面的一些額外的測試模板
template<class T> struct traits;
template<> struct traits<char  >{static const char*name(){return "char"
;}};
template<> struct traits<int   >{static const char*name(){return "int"
;}};
template<> struct traits<short >{static const char*name(){return "short"
;}};
template<> struct traits<long  >{static const char*name(){return "long"
;}};
template<> struct traits<float >{static const char*name(){return "float"
;}};
template<> struct traits<double>{static const char*name(){return "double"
;}};
//
必須外加一層包裹層來傳遞額外的類型參數
template <class T> struct Config
{
       
template<size_t i> struct
Function
        {
               
static void
execute()
                {
                       
//
你的代碼在這裏編寫
                        typedef typename xcl::type<T,i>::result CT;//
當前類型的意思
                        std::cout << i << " : " << traits<CT>::name() << std::endl;
                }
        };
};
int
main()
{
       
typedef xcl::cons<char
,
                xcl::cons<
int
,
                xcl::cons<
short
,
                xcl::cons<
long
,
                xcl::cons<
float
,
                xcl::cons<
double
,
                xcl::null_type> > > > > > CONS;
        std::cout <<
"CONS
類型串的長度爲:" << xcl::length<CONS>::value << std::endl;
        xcl::LOOP<Config<CONS>::Function,
0,xcl::length<CONS>::value,1
>::execute();
       
return 0
;
}
#endif//CODE1
////////////////////////////////////////////////////////////////////////////////
//
該程序的運行結果爲:
/*******************************************************************************
CONS
類型串的長度爲:6
0 : char
1 : int
2 : short
3 : long
4 : float
5 : double
*******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
#if 0

   
CODE1中的代碼可以看出,我們已經成功的實現了類型的循環遍歷。之所以需要實現
這種功能是因爲這種功能能夠在下一章中對自動生成的代碼執行任意的操作,這一點在自
動生成代碼上非常重要,如果這一點沒有實現,C++模板元自動生成的代碼和C宏生成的代
碼相比將不會有什麼神奇指出。這也是我將這段內容安排在這一章的主要原因。可能您還
沒有認識到這裏的代碼的重要性,不過這沒什麼關係,我會在後續的章節中詳細說明這裏
的類型遍歷的使用過程,並實現動態代碼和靜態代碼的連接。這樣之後您自然就會了解本
章的意義所在了:)
   
   
下面給出CODE1中的頭文件meta.h的內容如下:

#endif
#ifdef CODE_NOTE
#pragma once
namespace
pandaxcl{
       
//////////////////////////////////////////////////////////////////////
        template <bool Condition,class Then,class
Else>
       
struct
IF
        {
               
typedef Then result;//
Then類型作爲條件爲真的返回值(返回值爲類型)
        };
       
template<class Then,class
Else>
       
struct IF<false
,Then,Else>
        {
               
typedef Else result;//
Else類型作爲條件爲假的返回值(返回值爲類型)
        };
       
//////////////////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////////////
        ////
加入一個外覆層來傳遞額外的模板參數
        template <template<size_t>class Function,size_t start,size_t finish,size_t step>
       
struct
LOOP
        {
               
//
爲了能夠正確的計算出實際的循環終止變量,需要對給定的終止變量
                //
進行計算,以滿足正確的循環語義
                enum{real_finish=(finish/step*step+start)};
               
static void
execute()
                {
                        LOOP_BODY<real_finish,
true
>::execute();
                }
               
//
下面的這個模板函數是爲了能夠實現靜態代碼和動態代碼連接
                template <class EnvironmentType>
               
static void
execute(EnvironmentType&e)
                {
                        LOOP_BODY<real_finish,
true
>::execute(e);
                }
       
private
:
               
//
引入了一個布爾型的模板參數用來確定循環的終止條件
                template <size_t i,bool> struct LOOP_BODY
                {
                       
static void
execute()
                        {
                                LOOP_BODY<i-step,(i-step>start)>::execute();
                                Function<i-step>::execute();
                        }
                       
//
下面的這個模板函數是爲了能夠實現靜態代碼和動態代碼連接
                        template <class EnvironmentType>
                       
static void
execute(EnvironmentType&e)
                        {
                                LOOP_BODY<i-step,(i-step>start)>::execute(e);
                                Function<i-step>::execute(e);
                        }
                };
               
//
循環的終止語句,停止遞歸以結束循環
                template <size_t i> struct LOOP_BODY<i,false>
                {
                       
static void
execute(){}
                       
//
下面的這個模板函數是爲了能夠實現靜態代碼和動態代碼連接
                        template <class EnvironmentType>
                       
static void
execute(EnvironmentType&e){}
                };
        };
       
//
爲了模板化必須將原來的輸出函數做成一個模板結構體
        //template<size_t n> struct Function
        //{
        //      static void execute()
        //      {
        //              //
你的代碼在這裏編寫
        //      }
        //};
        //////////////////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////////////
        //cons
的實現,採用和STL類似的類型命名方式
        template <class FirstType,class SecondType>
       
struct
cons
        {
               
typedef
FirstType  first_type;
               
typedef
SecondType second_type;
        };
       
struct null_type;//
類型串終結符
        //
下面是兩個爲了實現靜態類型循環所需要的靜態元函數
        //length
元函數的實現
        template<class Type>struct length;
       
template<>struct
length<null_type>
        {
//
返回值爲整數,命名爲value
                enum{value=0};
        };
       
template<class FirstType,class
SecondType>
       
struct
length<cons<FirstType,SecondType> >
        {
//
返回值爲整數,命名爲value
                enum{value=1+length<SecondType>::value};
        };
       
//type
元函數的實現
        template<class Cons,size_t index>struct type;
       
template<class FirstType,class
SecondType>
       
struct type<cons<FirstType,SecondType>,0
>
        {
//
返回值爲類型,命名爲result
                typedef FirstType result;
        };
       
template<class FirstType,class SecondType,size_t
i>
       
struct
type<cons<FirstType,SecondType>,i>
        {
//
返回值爲類型,命名爲result
                typedef typename type<SecondType,i-1>::result result;
        };
       
//////////////////////////////////////////////////////////////////////
}//namespace pandaxcl{
#endif//CODE_NOTE
#if 0

   
從上面的meta.h文件中我們還可以看出,LOOP靜態循環代碼裏面比前一篇文章中的
LOOP
循環多了一些額外的代碼,這是爲了能夠實現靜態代碼和動態代碼的連接的,這種連
接通常是通過函數參數的形式實現的。至於爲什麼需要這一點將會在下一篇文章的代碼自
動生成一章中進行討論。(敬請關注!)

   
本章完。

   
未完,待續...

#endif

#if 0

   
這一章,我們將要開始的討論C++裏面的代碼生成技術。說起代碼生成技術,實際上這
並不是C++的專利,作爲C++子集的C語言早就已經使用了一定的代碼生成技術,這就是C
。我想C宏大家應該非常熟悉了吧,特別是能夠實現帶參數的宏使得大量的庫利用這種技術
來生成那些格式重複的代碼,例如:微軟的MFC庫,跨平臺的GUIwxWidgetBoost庫等等
都使用了C宏。雖然C宏在這些庫裏面扮演了非常重要的角色,並且仍將扮演非常重要的角
色,但是也不得不說:C宏存在着很大的問題。最基礎的就是類型不安全性,這也是C++
面出現模板語素的一個比較重要的原因。更重要的是使用C宏生成的代碼僅僅只是實現了簡
單的格式填空能力,並不能表達特定的算法。正是C宏的表達設計思想的不足限制了C宏的
使用範圍。

   
說起C++模板的代碼生成能力,說起來這也是一種巧合,自從90年代初期第一個C++
板元程序(用來在編譯期輸出質數)被發現以來,C++迷們對模板元程序的研究就熱鬧起來
了,並出現了大量的關於C++模板元程序的文獻。在這裏我所介紹的模板元代碼生成技術主
要參考了<<Modern C++ Design>>一書的GenScatterHierarchy結構,並對這種結構進行了
擴展應用,採用了前面的LOOP靜態循環實現對這種結構生成的代碼的操作,從而完成了一
C++普通類的自動生成過程。所謂的C++普通類指的是一般的手工直接編寫的一個類,這
種類通常包含成員變量,生成成員變量的過程可以由GenScatterHierarchy結構完成,但是
僅僅有了成員變量還不能成爲一個C++類,或許成爲結構體更合適;另外普通類一般還包含
了成員函數,這種成員函數的自動生成就不能通過Loki庫來實現自動生成了,雖然Loki
GenLinearHierarchy結構可以生成函數接口,但是函數體裏面的內容就不能夠隨心所欲
的編寫了。這後面的一點正是在本文中將要進行詳細討論的。

   
好了,現在我們來分析前面的章節中介紹的模板元技術中已經蘊涵的代碼生成技術。
實際上LOOP靜態循環中已經實現了靜態函數的自動生成,也就是說,編譯器在編譯的時候
確確實實是看到了循環所產生的所有的靜態函數,而並不是運行的時候進行的函數遞歸調用
。下面我們來看看C++裏面的多繼承現象和參數化繼承現象:

#endif

#ifdef CODE_NOTE
//
多繼承現象
class Base1{};
class
Base2{};
class
Base3{};
class Derived:public Base1,public Base2,public
Base3{};
//
模板參數化繼承現象:
template <class T>Base{};
class Derived:public Base<char>,public Base<int>,public Base<float
>{};
#endif//CODE_NOTE

#if 0

   
從上面的多繼承和參數化的多繼承我們可以得到什麼靈感呢?如果沒有,那麼再考慮
一下上一章中所介紹的類型串類型,^_^這時候有沒有靈感了呢?呵呵,實際上上面的代碼
中的參數化多繼承的基類就是一個類型遍歷過程,針對每一個類型,用Base包裹住每一個
類型並作爲Derived類的基類,這樣就可以生成一個自己定製的類了。如果能夠使這個過程
自動化,那麼我們就可以認爲代碼被自動生成了。

   
現在考慮一下上面的自動化過程所需要的輸入和輸出分別是什麼:

   
輸入:一個cons類型串記錄所有的需要的類型,一個包裹模板類
   
   
輸出:生成一個由所有的cons類型串中的類型作爲模板參數的包裹類作爲基類的類

   
這樣如果在包裹類裏面定義了一個模板參數類型的成員變量,那麼生成的類中就有所
有的這些類型的變量,也就是說這些變量都成了生成的類的成員變量。
   
    
好了,說到這裏,我們來看看具體的實現過程是怎樣的:

#endif

#ifdef CODE_NOTE
//
下面是實現代碼自動生成的模板元函數,主要參考了Loki的代碼
//
爲了撤銷和重做庫的獨立性,將該功能從Loki庫中提取出來
template<class T,template<class>class Unit>
struct scatter : public
Unit<T>
{
};
template<class H,class T,template<class>class
Unit>
struct
scatter<cons<H,T>,Unit>
        :
public
scatter<H,Unit>
        ,
public
scatter<T,Unit>
{
       
typedef
cons<H,T> cons_type;
};
//
下面的null_type參看前一章中的代碼
template<template<class>class Unit>
struct
scatter<null_type,Unit>
{
};
#endif//CODE_NOTE

#if 0

   
在給出具體的測試代碼之前還需要做一件事情,那就是將上面的scatter代碼放到meta.h文件中
便於代碼組織,也是爲了使用前面的類型null_type。關於本文所使用的meta.h文件僅僅只是在前一
章的meta.h文件的末尾追加了上面的scatter元函數,詳細的內容在本文的最後給出。下面看看如何
使用上面的模板元函數scatter來自動生成代碼,見CODE1

#endif

#ifdef CODE1
//
下面的是測試代碼
#include <iostream>
#include "meta.h"//
這裏的meta.h文件內容比上一章的內容多了一些,見本文末尾
namespace xcl=pandaxcl;
template <class T> struct
Unit
{
        T _value;
//
成員變量
};
template <class
Cons>
class Class:public
xcl::scatter<Cons,Unit>
{
//
注意這裏沒有任何的成員函數
};
int
main()
{
       
typedef xcl::cons<char
,
                xcl::cons<
int
,
                xcl::cons<
short
,
                xcl::cons<
long
,
                xcl::cons<
float
,
                xcl::cons<
double
,
                xcl::null_type> > > > > >CONS;
        Class<CONS> var;
//
聲明一個類型變量
        std::cout << sizeof(var) << std::endl;
        std::cout <<
sizeof
(Class<CONS>) << std::endl;
       
//
下面的這些代碼的成功編譯標誌着上面的過程確確實實產生了代碼
        static_cast<Unit<char  >&>(var)._value = 1;
       
static_cast<Unit<int   >&>(var)._value = 1
;
       
static_cast<Unit<short >&>(var)._value = 1
;
       
static_cast<Unit<long  >&>(var)._value = 1
;
       
static_cast<Unit<float >&>(var)._value = 1
;
       
static_cast<Unit<double>&>(var)._value = 1
;
       
return 0
;
}
#endif//CODE1
////////////////////////////////////////////////////////////////////////////////
//
該程序的運行結果如下:
/*******************************************************************************
48
48
*******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
#if 0

   
從上面的程序中我們可以看出代碼生成過程確實是成功完成了,但是我們還應該注意
到上面的類型串中的的類型是絕對不允許重複的,否則後面的static_cast靜態轉型將會出
現模棱兩可的情況。這一點在Loki庫已經成功的解決了,但是在我們將要討論的撤銷和重
做庫中並不會出現這種情況,所以爲了使得代碼儘可能的簡單,我們就採用最簡單的方式
。因爲簡單的就是最好的嘛!實際上通過這種簡單的類型串規定再通過外覆一個包裹層同
樣可以解決類型重複的問題。

   
上面的生成的類中已經具備了成員變量,但這僅僅相當於實現了一種結構體,離真正
的使用還有一段距離。因爲生成的類中沒有成員函數,只有在有了成員函數之後纔可以真
正的實用化,這就是前面的章節中介紹的cons類型和靜態LOOP循環的聯合使用發揮威力的
時候了。好,首先讓我們來看一個具體的問題:

   
考慮下面的自動生成的類:

#endif
#ifdef CODE_NOTE
template<class T> struct Unit:public
std::vector<T>{};
template<class Cons> class Class : public
xcl::scatter<Cons,Unit>
{
public
:
       
//
仔細考慮一下下面的這個成員函數應該如何實現?
        //
該成員函數就是爲了輸出自動生成的類中的所有的成員變量
        //
std::vector容器成員變量)的內容。
        void print(){}
};
#endif//CODE_NOTE
#if 0

   
從上面自動生成的類來說,爲了能夠自動的根據類型將所有的成員變量的內容都
進行輸出,需要寫一個print成員函數,這個成員函數能夠根據類型串的不同而自動的
生成相應的處理代碼,關於這一點的實現參見代碼CODE2

#endif
#ifdef CODE2
#include <iostream>
#include <iterator>
#include <vector>
#include <complex>
#include <string>
#include "meta.h"//
見本文末尾
namespace xcl=pandaxcl;
template<class T> struct Unit:public
std::vector<T>{};
template<class Cons> class Class : public
xcl::scatter<Cons,Unit>
{
       
template<size_t i> struct
PRINT
        {
               
template<class
EnvironmentType>
               
static void
execute(EnvironmentType&e)
                {
                       
//
你的代碼在這裏編寫
                        typedef typename xcl::type<Cons,i>::result CT;
                        Unit<CT> &v =
static_cast
<Unit<CT>&>(e);
                        std::copy(v.begin(),v.end(),std::ostream_iterator<CT>(std::cout,
" "
));
                        std::cout << std::endl;
//
用來分開不同類型的數據
                }
        };
public
:
       
//
下面是成員函數得實現
        void print()
        {
               
//
通過參數傳遞實現了靜態代碼和動態代碼的連接
                xcl::LOOP<PRINT,0,xcl::length<Cons>::value,1>::execute(*this);
        }
};
int
main()
{
        {
//
這是一個自動生成類的測試
                typedef xcl::cons<char,
                        xcl::cons<
int
,
                        xcl::cons<
float
,
                        xcl::null_type> > > CONS;
                Class<CONS> var;
               
//
在輸出之前需要初始化var變量
                static_cast<Unit<char>&>(var).push_back('A');
               
static_cast<Unit<char>&>(var).push_back('B'
);
               
static_cast<Unit<char>&>(var).push_back('C'
);

               
static_cast<Unit<int>&>(var).push_back(1
);
               
static_cast<Unit<int>&>(var).push_back(2
);
               
static_cast<Unit<int>&>(var).push_back(3
);
               
static_cast<Unit<int>&>(var).push_back(4
);

               
static_cast<Unit<float>&>(var).push_back(1.1
);
               
static_cast<Unit<float>&>(var).push_back(2.2
);
               
static_cast<Unit<float>&>(var).push_back(3.3
);
               
static_cast<Unit<float>&>(var).push_back(4.4
);
               
//
輸出所有的成員變量的內容
                var.print();
        }
        {
//
這是另一個自動生成類的測試
                //
修改了類型串之後不需要修改原來的print成員就可以實現所有的輸出
                typedef std::complex<float> COMPLEX;
               
typedef xcl::cons<char
,
                        xcl::cons<COMPLEX,
                        xcl::cons<std::string,
                        xcl::null_type> > > CONS;
                Class<CONS> var;
               
//
在輸出之前需要初始化var變量
                static_cast<Unit<char>&>(var).push_back('A');
               
static_cast<Unit<char>&>(var).push_back('B'
);
               
static_cast<Unit<char>&>(var).push_back('C'
);

               
static_cast<Unit<COMPLEX>&>(var).push_back(COMPLEX(1.1,0.1
));
               
static_cast<Unit<COMPLEX>&>(var).push_back(COMPLEX(2.2,0.2
));
               
static_cast<Unit<COMPLEX>&>(var).push_back(COMPLEX(3.3,0.3
));
               
static_cast<Unit<COMPLEX>&>(var).push_back(COMPLEX(4.4,0.4
));

               
static_cast<Unit<std::string>&>(var).push_back("
熊春雷");
               
static_cast<Unit<std::string>&>(var).push_back("
熊貓"
);
               
static_cast<Unit<std::string>&>(var).push_back("
國寶"
);
               
static_cast<Unit<std::string>&>(var).push_back("
開心"
);
               
static_cast<Unit<std::string>&>(var).push_back("pandaxcl"
);
               
//
輸出所有的成員變量的內容
                var.print();
        }
       
return 0
;
}
#endif//CODE2
////////////////////////////////////////////////////////////////////////////////
//
該程序的運行結果如下:
/*******************************************************************************
A B C
1 2 3 4
1.1 2.2 3.3 4.4
A B C
(1.1,0.1) (2.2,0.2) (3.3,0.3) (4.4,0.4)
熊春雷 熊貓 國寶 開心 pandaxcl
*******************************************************************************/
////////////////////////////////////////////////////////////////////////////////

#if 0

   
CODE2中可以看出,自動化的類Class可以根據傳遞的類型串自動的調整成員變量數
量,同時也會自動調整print成員函數的功能。其中後者是本文所介紹的方法,也是模板元
函數用來進行自動化編程的關鍵。通過print成員函數的演示,這裏你是否有很多的想法呢
?呵呵:),是啊既然可以讓成員函數也能夠實現自動化,那麼採用C++模板元編寫自動化的
類將是一件可行的事情,這就看各位的發揮了。

   
拋磚引玉的過程基本上已經完備,接下來就是介紹一下爲什麼需要自動化編程的背景
了。我們在編寫程序代碼的時候通常是將各種不同的功能分成一個個的小模塊,當一個個
的模塊編寫好了之後就可以以庫的形式提供給客戶端使用,同時還需要附帶一個非常詳細
的文檔,用來說明如何使用這個庫。通常來說,每一個庫的文檔都是不同的,都有各自不
同的特殊要求,這就要客戶端非常熟悉所使用的庫,但是通常來說,庫的編寫者非常熟悉
自己編寫的庫,而庫的使用者卻不熟悉這個庫。在這裏介紹的自動化編程就是將這種矛盾
進行緩解的一種努力方式。庫的編寫者將庫的使用規則儘可能的編碼到庫中,留給庫的使
用者一個通用而簡單的使用界面,將那些特殊的規則留給庫的編寫者而不是庫的使用者不
僅可以降低庫的使用門檻,同時還可以大大減少使用庫的過程中發生錯誤的可能性。

   
以上是我的觀點,僅供參考。

   
本章完。

   
從本文還可以看出,文中實際上還留下了一個問題:自動生成代碼使用的類型串中不
允許有重複類型出現,這一點實際上是一個缺陷,將會在下一章中解決。但是作爲原理來
講,本章介紹的方法在下一章裏面用來解決重複類型的類型串生成代碼的時候也需要採用
了這裏的方法,所以非常有必要先在這裏介紹這種方法。在下一章裏面將會討論如何使用
本章介紹的不含重複類型的類型串生成代碼的技術用來解決含有重複類型的類型串生成代
碼的問題。注意下一章中採用的方法和Loki庫中採用的方法可不相同哦:)(敬請關注!

   
未完。待續...

#endif
#ifdef CODE_NOTE//
附錄:本文使用的meta.h文件內容
#pragma once
namespace
pandaxcl{
       
//////////////////////////////////////////////////////////////////////
        template <bool Condition,class Then,class
Else>
       
struct
IF
        {
               
typedef Then result;//
Then類型作爲條件爲真的返回值(返回值爲類型)
        };
       
template<class Then,class
Else>
        
struct IF<false
,Then,Else>
        {
               
typedef Else result;//
Else類型作爲條件爲假的返回值(返回值爲類型)
        };
       
//////////////////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////////////
        ////
加入一個外覆層來傳遞額外的模板參數
        template <template<size_t>class Function,size_t start,size_t finish,size_t step>
       
struct
LOOP
        {
               
//
爲了能夠正確的計算出實際的循環終止變量,需要對給定的終止變量
                //
進行計算,以滿足正確的循環語義
                enum{real_finish=(finish/step*step+start)};
               
static void
execute()
                {
                        LOOP_BODY<real_finish,
true
>::execute();
                }
               
//
下面的這個模板函數是爲了能夠實現靜態代碼和動態代碼連接
                template <class EnvironmentType>
               
static void
execute(EnvironmentType&e)
                {
                        LOOP_BODY<real_finish,
true
>::execute(e);
                }
       
private
:
               
//
引入了一個布爾型的模板參數用來確定循環的終止條件
                template <size_t i,bool> struct LOOP_BODY
                {
                       
static void
execute()
                        {
                                LOOP_BODY<i-step,(i-step>start)>::execute();
                                Function<i-step>::execute();
                        }
                       
//
下面的這個模板函數是爲了能夠實現靜態代碼和動態代碼連接
                        template <class EnvironmentType>
                       
static void
execute(EnvironmentType&e)
                        {
                                LOOP_BODY<i-step,(i-step>start)>::execute(e);
                                Function<i-step>::execute(e);
                        }
                };
               
//
循環的終止語句,停止遞歸以結束循環
                template <size_t i> struct LOOP_BODY<i,false>
                {
                       
static void
execute(){}
                       
//
下面的這個模板函數是爲了能夠實現靜態代碼和動態代碼連接
                        template <class EnvironmentType>
                       
static void
execute(EnvironmentType&e){}
                };
        };
       
//
爲了模板化必須將原來的輸出函數做成一個模板結構體
        //template<size_t i> struct Function
        //{
        //      static void execute()
        //      {
        //              //
你的代碼在這裏編寫
        //      }
        //};
        //////////////////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////////////
        //cons
的實現,採用和STL類似的類型命名方式
        template <class FirstType,class SecondType>
       
struct
cons
        {
               
typedef
FirstType  first_type;
               
typedef
SecondType second_type;
        };
       
struct null_type;//
類型串終結符
        //
下面是兩個爲了實現靜態類型循環所需要的靜態元函數
        //length
元函數的實現
        template<class Type>struct length;
        
template<>struct
length<null_type>
        {
//
返回值爲整數,命名爲value
                enum{value=0};
        };
       
template<class FirstType,class
SecondType>
       
struct
length<cons<FirstType,SecondType> >
        {
//
返回值爲整數,命名爲value
                enum{value=1+length<SecondType>::value};
        };
       
//type
元函數的實現
        template<class Cons,size_t index>struct type;
       
template<class FirstType,class
SecondType>
       
struct type<cons<FirstType,SecondType>,0
>
        {
//
返回值爲類型,命名爲result
                typedef FirstType result;
        };
       
template<class FirstType,class SecondType,size_t
i>
       
struct
type<cons<FirstType,SecondType>,i>
        {
//
返回值爲類型,命名爲result
                typedef typename type<SecondType,i-1>::result result;
        };
       
//////////////////////////////////////////////////////////////////////
        //
下面是實現代碼自動生成的模板元函數,主要參考了Loki的代碼
        //
爲了撤銷和重做庫的獨立性,將該功能從Loki庫中提取出來
        template<class T,template<class>class Unit>
       
struct scatter : public
Unit<T>
        {
        };
       
template<class H,class T,template<class>class
Unit>
       
struct
scatter<cons<H,T>,Unit>
                :
public
scatter<H,Unit>
                ,
public
scatter<T,Unit>
        {
               
typedef
cons<H,T> cons_type;
        };
       
//
下面的null_type參看前一章中的代碼
        template<template<class>class Unit>
       
struct
scatter<null_type,Unit>
        {
        };
       
//////////////////////////////////////////////////////////////////////
}//namespace pandaxcl{
#endif//CODE_NOTE//
附錄

#if 0

   
在上一篇文章裏面討論了C++裏面的代碼生成技術,使用的是scatter,不過上一篇文
章裏面也提到了,前一篇文章裏面討論的代碼生成模板scatter使用的類型串絕對不允許
重複。其實上一篇中的scatter使用由重複的類型的類型串也是能夠正常生成代碼的,不
過產生的代碼卻不能將類型重複的變量分辨出來,這樣生成的代碼就沒有了什麼實際意義
,所以在這一章中將要解決的問題是:重新編寫一個可以使用重複類型的類型串生成代碼
,並且能夠採用一定的方法將這些生成的變量分辨出來。

   
那麼該如何編寫這裏需要的代碼呢?上一章裏面的scatter見下面的代碼:

#endif
#ifdef CODE_NOTE
template<class T,template<class>class
Unit>
struct scatter : public
Unit<T>
{
};
template<class H,class T,template<class>class
Unit>
struct
scatter<cons<H,T>,Unit>
        :
public
scatter<H,Unit>
        ,
public
scatter<T,Unit>
{
       
typedef
cons<H,T> cons_type;
};
template<template<class>class
Unit>
struct
scatter<null_type,Unit>
{
};
#endif//CODE_NOTE
#if 0

   
雖然上面的代碼生成機制生成的重複類型的代碼不能夠通過靜態轉型(static_cast)
辨出來,但是上面的代碼卻給出另外一種重要的思想:使用不含有重複類型的類型串採用
上面的代碼生成技術生成的代碼卻可以使用靜態轉型(static_cast)分辨出來。前一章裏面
的例子都採用了這種思想。那麼現在我們可不可以同樣使用這種思想編寫出一個可以使用
重複類型的類型串生成代碼,並且能夠通過靜態轉型(static_cast)方法將這些生成的變量
分辨出來呢?答案是肯定的,在這裏同樣是採用前面所討論的多加一層包裹的方法來實現。

   
我們知道對於下面的模板類:

#endif
#ifdef CODE_NOTE
template <size_t
>
struct
Struct
{
};
Struct<
0
> a;
Struct<
1
> b;
Struct<
2
> c;
Struct<
3
> d;
//... ...
#endif//CODE_NOTE
#if 0

   
上面的Struct模板的模板參數不同將會生成不同的類型。例如:上面的Struct<0>
Struct<1>
Struct<2>Struct<3>是四種不同的類型。這一點就像是給這個模板編號讓這
個編號的不同來區分出不同的類型。正是因爲這種現象的存在我們纔有了這樣的思想:在
新的scatter代碼生成機制中給每一個串中的類型聲明變量的時候額外添加一個類型索引號
。這樣就可以使用靜態轉型方法分辨出同類型的變量了。見代碼CODE1所示:

#endif
#ifdef CODE1
#include <iostream>
#include "meta.h"//
爲了引入pandaxcl::cons
//
下面的代碼僅僅是爲了測試產生代碼的索引號和類型是否匹配而引入的特性類模板
//
說得簡單點,這裏所謂的特性類就是一種大型的switchcase結構,只不過是跟據
//
類型的不同由編譯器來自動選擇相應的模板。在這裏討論的特性類實際上可以應用
//
到撤銷和重做框架的序列化方案中去,當然這是爲了保存類型名稱了。
template <class T> struct traits;
template <> struct traits<char>{static const char*name(){return "char"
;}};
template <> struct traits<int >{static const char*name(){return "int "
;}};
////////////////////////////////////////////////////////////
//
下面的這個產生代碼的格式還可以使用索引號得到基元對象注意了:下面的遞歸模板的
//
終結條件並不是第一個模板參數size_t而是第二個模板參數T,這一點很重要,因爲前面
//
的文章中討論的都是以數字0作爲終結條件的。所以這裏需要特別說明一下,另外也可以
//
給你一點靈感:)
template<size_t i,class T,template<class>class Unit>
struct scatter_helper : public
Unit<T>
{
       
//
下面的構造函數僅僅是爲了輸出索引號對應的類型信息而引入的
        scatter_helper()
        {
                std::cout << traits<T>::name() <<
" : "
<< i << std::endl;
        }
};
template<size_t i,class H,class T,template<class>class
Unit>
struct
scatter_helper<i,pandaxcl::cons<H,T>,Unit>
        :
public
scatter_helper<i,H,Unit>
        ,
public scatter_helper<i+1,T,Unit>//
遞增的爲每一個類型添加一個額外的索引號
{
       
typedef
pandaxcl::cons<H,T> cons_type;
};
//
下面的null_type參看前一章中的代碼
template<size_t i,template<class>class Unit>
struct
scatter_helper<i,pandaxcl::null_type,Unit>
{
};
//
爲了和前面章節中討論的scatter兼容也是爲了方便使用,這裏重新對scatter_helper進行
//
包裝生成了一個模板類scatter,這樣前面使用的任何方法針對於這個新的scatter類同樣
//
適用。
template <class Cons,template<class>class Unit>
struct scatter:public scatter_helper<0
,Cons,Unit>
{
       
typedef
Cons cons_type;
};
//
下面的函數用來根據類型索引號來得到相應的基元類型
template <size_t i,class Cons,template<class>class Unit>
scatter_helper<i,
typename
pandaxcl::type<Cons,i>::result,Unit>&
        field(scatter<Cons,Unit>&obj)
{
        
typedef typename
pandaxcl::type<Cons,i>::result CT;
       
typedef
scatter_helper<i,CT,Unit> RT;
       
return static_cast
<RT&>(obj);
};
template <size_t i,class Cons,template<class>class
Unit>
const scatter_helper<i,typename
pandaxcl::type<Cons,i>::result,Unit>&
        field(
const
scatter<Cons,Unit>&obj)
{
       
typedef typename
pandaxcl::type<Cons,i>::result CT;
       
typedef
scatter_helper<i,CT,Unit> RT;
       
return static_cast<const
RT&>(obj);
};
//////////////////////////////////////////////////////////////////////
//
下面是測試代碼
namespace xcl=pandaxcl;
template <class T> struct
TestUnit
{
        T _value;
};
int
main()
{
       
typedef xcl::cons<int
,
                xcl::cons<
char
,
                xcl::cons<
char
,
                xcl::cons<
int
,
                xcl::null_type> > > >CONS;
        std::cout <<
"========scatter_helper========"
<<std::endl;
        {
                scatter_helper<
0
,CONS,TestUnit> v;
               
static_cast<scatter_helper<0,int ,TestUnit>&>(v)._value = 10
;
                
static_cast<scatter_helper<1,char,TestUnit>&>(v)._value = 'B'
;
               
static_cast<scatter_helper<2,char,TestUnit>&>(v)._value = 'C'
;
               
static_cast<scatter_helper<3,int ,TestUnit>&>(v)._value = 11
;
                std::cout <<
static_cast<scatter_helper<0,int ,TestUnit>&>(v)._value << " "
;
                std::cout <<
static_cast<scatter_helper<1,char,TestUnit>&>(v)._value << " "
;
                std::cout <<
static_cast<scatter_helper<2,char,TestUnit>&>(v)._value << " "
;
                std::cout <<
static_cast<scatter_helper<3,int ,TestUnit>&>(v)._value << " "
;
        }
        std::cout << std::endl <<
"========field(scatter&)======="
<<std::endl;
       
////////////////////////////////////////////////////////////////////////////////
        {
                scatter<CONS,TestUnit> v;
                field<
0>(v)._value = 20
;
                field<
1>(v)._value = 'F'
;
                field<
2>(v)._value = 'G'
;
                field<
3>(v)._value = 21
;
                std::cout << field<
0>(v)._value << " "
;
                std::cout << field<
1>(v)._value << " "
;
                std::cout << field<
2>(v)._value << " "
;
                std::cout << field<
3>(v)._value << " "
;
        }
        std::cout << std::endl <<
"=====field(const scatter&)===="
<<std::endl;
        {
                scatter<CONS,TestUnit> v;
                field<
0>(v)._value = 30
;
                field<
1>(v)._value = 'J'
;
                field<
2>(v)._value = 'K'
;
                field<
3>(v)._value = 31
;
                
//
測試常量情況下的field輔助函數
                const scatter<CONS,TestUnit>&cv = v;
                std::cout << field<
0>(cv)._value << " "
;
                std::cout << field<
1>(cv)._value << " "
;
                std::cout << field<
2>(cv)._value << " "
;
                std::cout << field<
3>(cv)._value << " "
;
        }
        std::cout << std::endl <<
"=============================="
<<std::endl;
       
return 0
;
}
#endif//CODE1
////////////////////////////////////////////////////////////////////////////////
//
該程序運行結果如下:
/*******************************************************************************
========scatter_helper========
int  : 0
char : 1
char : 2
int  : 3
10 B C 11
========field(scatter&)=======
int  : 0
char : 1
char : 2
int  : 3
20 F G 21
=====field(const scatter&)====
int  : 0
char : 1
char : 2
int  : 3
30 J K 31
==============================
*******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
#if 0

   
從運行結果可以看出我們已經成功的解決了本文開頭所提出的問題。這個新的代碼產生
模板已經寫出來了。爲了以後使用的方便需要將這個模板類加入到meta.h中。不過本文中使
用的meta.h並沒有包含這裏的scatter模板類,見附錄所示。

   
本章完。

   
實際上C++模板元編程的基礎與應用中的基礎篇已經寫完了,從下一章開始將會討論這種
編程技術的一些應用專題。下一章裏面將會涉及到的專題是類型特性trait。(敬請關注!

   
未完,待續...

#endif
#ifdef CODE_NOTE//
附錄:本文采用的meta.h文件內容如下:
#pragma once
namespace
pandaxcl{
       
//////////////////////////////////////////////////////////////////////
        template <bool Condition,class Then,class
Else>
       
struct
IF
        {
                
typedef Then result;//
Then類型作爲條件爲真的返回值(返回值爲類型)
        };
       
template<class Then,class
Else>
       
struct IF<false
,Then,Else>
        {
               
typedef Else result;//
Else類型作爲條件爲假的返回值(返回值爲類型)
        };
       
//////////////////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////////////
        ////
加入一個外覆層來傳遞額外的模板參數
        template <template<size_t>class Function,size_t start,size_t finish,size_t step>
       
struct
LOOP
        {
               
//
爲了能夠正確的計算出實際的循環終止變量,需要對給定的終止變量
                //
進行計算,以滿足正確的循環語義
                enum{real_finish=(finish/step*step+start)};
               
static void
execute()
                {
                        LOOP_BODY<real_finish,
true
>::execute();
                }
               
//
下面的這個模板函數是爲了能夠實現靜態代碼和動態代碼連接
                template <class EnvironmentType>
               
static void
execute(EnvironmentType&e)
                {
                        LOOP_BODY<real_finish,
true
>::execute(e);
                }
       
private
:
               
//
引入了一個布爾型的模板參數用來確定循環的終止條件
                template <size_t i,bool> struct LOOP_BODY
                {
                       
static void
execute()
                        {
                                LOOP_BODY<i-step,(i-step>start)>::execute();
                                Function<i-step>::execute();
                        }
                       
//
下面的這個模板函數是爲了能夠實現靜態代碼和動態代碼連接
                        template <class EnvironmentType>
                       
static void
execute(EnvironmentType&e)
                        {
                                LOOP_BODY<i-step,(i-step>start)>::execute(e);
                                Function<i-step>::execute(e);
                        }
                };
               
//
循環的終止語句,停止遞歸以結束循環
                template <size_t i> struct LOOP_BODY<i,false>
                {
                       
static void
execute(){}
                       
//
下面的這個模板函數是爲了能夠實現靜態代碼和動態代碼連接
                        template <class EnvironmentType>
                       
static void
execute(EnvironmentType&e){}
                };
        };
       
//
爲了模板化必須將原來的輸出函數做成一個模板結構體
        //template<size_t i> struct Function
        //{
        //      static void execute()
        //      {
        //              //
你的代碼在這裏編寫
        //      }
        //};
        //////////////////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////////////
        //cons
的實現,採用和STL類似的類型命名方式
        template <class FirstType,class SecondType>
       
struct
cons
        {
               
typedef
FirstType  first_type;
               
typedef
SecondType second_type;
        };
       
struct null_type;//
類型串終結符
        //
下面是兩個爲了實現靜態類型循環所需要的靜態元函數
        //length
元函數的實現
        template<class Type>struct length;
       
template<>struct
length<null_type>
        {
//
返回值爲整數,命名爲value
                enum{value=0};
        };
       
template<class FirstType,class
SecondType>
       
struct
length<cons<FirstType,SecondType> >
        {
//
返回值爲整數,命名爲value
                enum{value=1+length<SecondType>::value};
        };
       
//type
元函數的實現
        template<class Cons,size_t index>struct type;
       
template<class FirstType,class
SecondType>
       
struct type<cons<FirstType,SecondType>,0
>
        {
//
返回值爲類型,命名爲result
                typedef FirstType result;
        };
       
template<class FirstType,class SecondType,size_t
i>
       
struct
type<cons<FirstType,SecondType>,i>
        {
//
返回值爲類型,命名爲result
                typedef typename type<SecondType,i-1>::result result;
        };
       
//index
元函數根據類型得到類型串中該類型位置
        template<class Cons,class Test>struct index;
       
template<class Test> struct
index<null_type,Test>
        {
               
enum{value=-1
};
        };
       
template<class T,class Test> struct
index<cons<Test,T>,Test>
        {
               
enum{value=0
};
        };
       
template<class H,class T,class Test> struct
index<cons<H,T>,Test>
        {
       
private
:
               
enum
{temp=index<T,Test>::value};
       
public
:
               
enum{value=(temp==-1?-1:1
+temp)};
        };
       
//////////////////////////////////////////////////////////////////////
        //
下面是實現代碼自動生成的模板元函數,主要參考了Loki的代碼
        //
爲了撤銷和重做庫的獨立性,將該功能從Loki庫中提取出來
        template<class T,template<class>class Unit>
       
struct scatter : public
Unit<T>
        {
        };
       
template<class H,class T,template<class>class
Unit>
       
struct
scatter<cons<H,T>,Unit>
                :
public
scatter<H,Unit>
                ,
public
scatter<T,Unit>
        {
               
typedef
cons<H,T> cons_type;
        };
       
//
下面的null_type參看前一章中的代碼
        template<template<class>class Unit>
       
struct
scatter<null_type,Unit>
        {
        };
       
////////////////////////////////////////////////////////////////////// 
}//namespace pandaxcl{
#enidf//CODE_NOTE

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章