• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            大龍的博客

            常用鏈接

            統(tǒng)計(jì)

            最新評(píng)論

            模板元 --------- 轉(zhuǎn)

            摘要
            本文簡述了模板元編程技術(shù)的起源、概念和機(jī)制, 并介紹了模板元編程技術(shù)在Blitz++和Loki程序庫中的應(yīng)用。  
            關(guān)鍵字
            編譯期計(jì)算  模板元編程  Blitz++  Loki  
            導(dǎo)言  
            1994年,C++標(biāo)準(zhǔn)委員會(huì)在圣迭哥舉行的一次會(huì)議期間Erwin Unruh展示了一段可以產(chǎn)生質(zhì)數(shù)的代碼。這段代碼的特別之處在于質(zhì)數(shù)產(chǎn)生于編譯期而非運(yùn)行期,在編譯器產(chǎn)生的一系列錯(cuò)誤信息中間夾雜著從2到某個(gè)設(shè)定值之間的所有質(zhì)數(shù):  
            // Prime number computation by Erwin Unruh
            template <int i> struct D { D(void*); operator int(); };
            template <int p, int i> struct is_prime {
                enum { prim = (p%i) && is_prime<(i > 2 ? p : 0), i -1> :: prim };
            };
            template < int i > struct Prime_print {
                Prime_print<i-1> a;
                enum { prim = is_prime<i, i-1>::prim };
                void f() { D<i> d = prim; }
            };
            struct is_prime<0,0> { enum {prim=1}; };
            struct is_prime<0,1> { enum {prim=1}; };
            struct Prime_print<2> { enum {prim = 1}; void f() { D<2> d = prim; } };
            #ifndef LAST
            #define LAST 10
            #endif
            main () {
                Prime_print<LAST> a;
            }
            類模板D只有一個(gè)參數(shù)為void*的構(gòu)造器,而只有0才能被合法轉(zhuǎn)換為void*。1994年,Erwin Unruh采用Metaware 編譯器編譯出錯(cuò)信息如下(以及其它一些信息,簡短起見,它們被刪除了):  
            | Type `enum{}&acute; can&acute;t be converted to txpe `D<2>&acute; ("primes.cpp",L2/C25).
            | Type `enum{}&acute; can&acute;t be converted to txpe `D<3>&acute; ("primes.cpp",L2/C25).
            | Type `enum{}&acute; can&acute;t be converted to txpe `D<5>&acute; ("primes.cpp",L2/C25).
            | Type `enum{}&acute; can&acute;t be converted to txpe `D<7>&acute; ("primes.cpp",L2/C25).
            如今,上面的代碼已經(jīng)不再是合法的C++程序了。以下是Erwin Unruh親手給出的修訂版,可以在今天符合標(biāo)準(zhǔn)的C++編譯器上進(jìn)行編譯:  
            // Prime number computation by Erwin Unruh
            template <int i> struct D { D(void*); operator int(); };
            template <int p, int i> struct is_prime {
                enum { prim = (p==2) || (p%i) && is_prime<(i>2?p:0), i-1> :: prim };
            };
            template <int i> struct Prime_print {
            Prime_print<i-1> a;
                enum { prim = is_prime<i, i-1>::prim };
                void f() { D<i> d = prim ? 1 : 0; a.f();}
            };
            template<> struct is_prime<0,0> { enum {prim=1}; };
            template<> struct is_prime<0,1> { enum {prim=1}; };
            template<> struct Prime_print<1> {
                enum {prim=0};
                void f() { D<1> d = prim ? 1 : 0; };
            };
            #ifndef LAST
            #define LAST 18
            #endif
            main() {
                Prime_print<LAST> a;
                a.f();
            }
            在GNU C++ (MinGW Special) 3.2中編譯這段程序時(shí),編譯器將會(huì)給出如下出錯(cuò)信息(以及其它一些信息,簡短起見,它們被刪除了):  
            Unruh.cpp:12: initializing argument 1 of `D<i>::D(void*) [with int i = 17]'
            Unruh.cpp:12: initializing argument 1 of `D<i>::D(void*) [with int i = 13]'
            Unruh.cpp:12: initializing argument 1 of `D<i>::D(void*) [with int i = 11]'
            Unruh.cpp:12: initializing argument 1 of `D<i>::D(void*) [with int i = 7]'
            Unruh.cpp:12: initializing argument 1 of `D<i>::D(void*) [with int i = 5]'
            Unruh.cpp:12: initializing argument 1 of `D<i>::D(void*) [with int i = 3]'
            Unruh.cpp:12: initializing argument 1 of `D<i>::D(void*) [with int i = 2]'
            這個(gè)例子展示了可以利用模板實(shí)例化機(jī)制于編譯期執(zhí)行一些計(jì)算。這種通過模板實(shí)例化而執(zhí)行的特別的編譯期計(jì)算技術(shù)即被稱為模板元編程。
            順便說一句,因?yàn)榫幾g器的出錯(cuò)信息并未被標(biāo)準(zhǔn)化,所以,如果你在Visual C++、Borland C++等編譯器上看不到這么詳細(xì)的出錯(cuò)信息,請(qǐng)不必訝異。  
            一個(gè)可以運(yùn)行的模板元編程例子  
            模板元編程(Template Metaprogramming)更準(zhǔn)確的含義應(yīng)該是“編‘可以編程序的’程序”,而模板元程序(Template Metaprogram)則是“‘可以編程序的’程序”。也就是說,我們給出代碼的產(chǎn)生規(guī)則,編譯器在編譯期解釋這些規(guī)則并生成新代碼來實(shí)現(xiàn)我們預(yù)期的功能。  
            Erwin Unruh的那段經(jīng)典代碼并沒有執(zhí)行,它只是以編譯出錯(cuò)信息的方式輸出中間計(jì)算結(jié)果。讓我們來看一個(gè)可以運(yùn)行的模板元編程例子 — 計(jì)算給定整數(shù)的指定次方:  
            // xy.h
            //原始摸板
            template<int Base, int Exponent>
            class XY
            {
            public:
                enum { result_ = Base * XY<Base, Exponent-1>::result_ };
            };
            //用于終結(jié)遞歸的局部特化版
            template<int Base>
            class XY<Base, 0>
            {
            public:
                enum { result_ = 1 };
            };
            模板元編程技術(shù)之根本在于遞歸模板實(shí)例化。第一個(gè)模板實(shí)現(xiàn)了一般情況下的遞歸規(guī)則。當(dāng)用一對(duì)整數(shù)<X, Y>來實(shí)例化模板時(shí),模板XY<X, Y>需要計(jì)算其result_的值,將同一模板中針對(duì)<X, Y-1>實(shí)例化所得結(jié)果乘以X即可。第二個(gè)模板是一個(gè)局部特化版本,用于終結(jié)遞歸。  
            讓我們看看使用此模板來計(jì)算5^4 (通過實(shí)例化XY<5, 4>)時(shí)發(fā)生了什么:  
            // xytest.cpp
            #include <iostream>
            #include "xy.h"
            int main()
            {
                std::cout << "X^Y<5, 4>::result_ = " << XY<5, 4>::result_;
            }
            首先,編譯器實(shí)例化XY<5, 4>,它的result_為5 * XY<5, 3>::result_,如此一來,又需要針對(duì)<5, 3>實(shí)例化同樣的模板,后者又實(shí)例化XY<5, 2>…… 當(dāng)實(shí)例化到XY<5, 0>的時(shí)候,result_的值被計(jì)算為1,至此遞歸結(jié)束。  
            遞歸模板實(shí)例化的深度和終結(jié)條件  
            可以想象,如果我們以非常大的Y值來實(shí)例化類模板XY,那肯定會(huì)占用大量的編譯器資源甚至?xí)杆俸谋M可用資源(在計(jì)算結(jié)果溢出之前),因此,在實(shí)踐中我們應(yīng)該有節(jié)制地使用模板元編程技術(shù)。  
            雖然 C++標(biāo)準(zhǔn)建議的最小實(shí)例化深度只有17層,然而大多數(shù)編譯器都能夠處理至少幾十層,有些編譯器允許實(shí)例化至數(shù)百層,更有一些可達(dá)數(shù)千層,直至資源耗盡。  
            假如我們拿掉XY模板局部特化版本,情況會(huì)如何?  
            // xy2.h
            //原始摸板
            template<int Base, int Exponent>
            class XY
            {
            public:
                enum { result_ = Base * XY<Base, Exponent-1>::result_ };
            };
            測試程序不變:  
            // xytest2.cpp
            #include <iostream>
            #include "xy2.h"
            int main()
            {
                std::cout << "X^Y<5, 4>::result_ = " << XY<5, 4>::result_;
            }
            執(zhí)行如下編譯命令:  
            C:\>g++ -c xytest2.cpp  
            你將會(huì)看到遞歸實(shí)例化將一直進(jìn)行下去,直到達(dá)到編譯器的極限。  
            GNU C++ (MinGW Special) 3.2的默認(rèn)實(shí)例化極限深度為500層,你也可以手工調(diào)整實(shí)例化深度:  
            C:\>g++ -ftemplate-depth-3400 -c xytest2.cpp  
            事實(shí)上,就本例而言,g++ 3.2允許的實(shí)例化極限深度還可以再大一些(我的測試結(jié)果是不超過3450層)。  
            因此,在使用模板元編程技術(shù)時(shí),我們總是要給出原始模板的特化版(局部特化版或完全特化版或兼而有之),以作為遞歸模板實(shí)例化的終結(jié)準(zhǔn)則。  
            利用模板元編程技術(shù)解開循環(huán)  
            模板元編程技術(shù)最早的實(shí)際應(yīng)用之一是用于數(shù)值計(jì)算中的解循環(huán)。舉個(gè)例子,對(duì)一個(gè)數(shù)組進(jìn)行求和的常見方法是:  
            // sumarray.h
            template <typename T>
            inline T sum_array(int Dim, T* a)
            {
                T result = T();
                for (int i = 0; i < Dim; ++i)
                {
                    result += a;
                }
                return result;
            }
            這當(dāng)然可行,但我們也可以利用模板元編程技術(shù)來解開循環(huán):  
            // sumarray2.h
            // 原始模板
            template <int Dim, typename T>
            class Sumarray
            {
            public:
                static T result(T* a)
                {
                    return a[0] + Sumarray<Dim-1, T>::result(a+1);
                }
            };
            // 作為終結(jié)準(zhǔn)則的局部特化版
            template <typename T>
            class Sumarray<1, T>
            {
            public:
                static T result(T* a)
                {
                    return a[0];
                }
            };
            用法如下:  
            // sumarraytest2.cpp
            #include <iostream>
            #include "sumarray2.h"
            int main()
            {
                int a[6] = {1, 2, 3, 4, 5, 6};
                std::cout << " Sumarray<6>(a) = " << Sumarray<6, int>::result(a);
            }
            當(dāng)我們計(jì)算Sumarray<6, int>::result(a)時(shí),實(shí)例化過程如下:  
            Sumarray<6, int>::result(a)
            = a[0] + Sumvector<5, int>::result(a+1)
            = a[0] + a[1] + Sumvector<4, int>::result(a+2)
            = a[0] + a[1] + a[2] + Sumvector<3, int>::result(a+3)
            = a[0] + a[1] + a[2] + a[3] + Sumvector<2, int>::result(a+4)
            = a[0] + a[1] + a[2] + a[3] + a[4] + Sumvector<1, int>::result(a+5)
            = a[0] + a[1] + a[2] + a[3] + a[4] + a[5]
            可見,循環(huán)被展開為a[0]  + a[1] + a[2] + a[3] + a[4] + a[5]。這種直截了當(dāng)?shù)恼归_運(yùn)算幾乎總是比循環(huán)來得更有效率。  
            也許拿一個(gè)有著600萬個(gè)元素的數(shù)組來例證循環(huán)開解的優(yōu)勢可能更有說服力。生成這樣的數(shù)組很容易,有興趣,你不妨測試、對(duì)比一下。
            (感謝一位朋友的測試。他說 ,“據(jù)在Visual C++ 2003上實(shí)測編譯器應(yīng)當(dāng)進(jìn)行了尾遞歸優(yōu)化,可以不受上面說的遞歸層次的限制,然而連加的結(jié)果在數(shù)組個(gè)數(shù)達(dá)到4796之后就不再正確了,程序輸出了空行,已經(jīng)出錯(cuò)” — 2003年12月30日補(bǔ)充)
            模板元編程在數(shù)值計(jì)算程序庫中的應(yīng)用  
            Blitz++之所以“快如閃電”(這正是blitz的字面含義),離不開模板元程序的功勞。Blitz++淋漓盡致地使用了元編程技術(shù),你可以到這些文件源代碼中窺探究竟:  
            dot.h
            matassign.h
            matmat.h
            matvec.h
            metaprog.h
            product.h
            sum.h
            vecassign.h  
            讓我們看看Blitz++程序庫dot.h文件中的模板元程序:  
            template<int N, int I>
            class _bz_meta_vectorDot {
            public:
                enum { loopFlag = (I < N-1) ? 1 : 0 };
                template<class T_expr1, class T_expr2>
                static inline BZ_PROMOTE(_bz_typename T_expr1::T_numtype, _bz_typename T_expr2::T_numtype)
                f(const T_expr1& a, const T_expr2& b)
                {
                    return a[I] * b[I] + _bz_meta_vectorDot<loopFlag * N, loopFlag * (I+1)>::f(a,b);
                }
                template<class T_expr1, class T_expr2>
                static inline BZ_PROMOTE(_bz_typename T_expr1::T_numtype, _bz_typename T_expr2::T_numtype)
                f_value_ref(T_expr1 a, const T_expr2& b)
                {
                    return a[I] * b[I] + _bz_meta_vectorDot<loopFlag * N, loopFlag * (I+1)>::f(a,b);
                }
                template<class T_expr1, class T_expr2>
                static inline BZ_PROMOTE(_bz_typename T_expr1::T_numtype, _bz_typename T_expr2::T_numtype)
                f_ref_value(const T_expr1& a, T_expr2 b)
                {
                    return a[I] * b[I] + _bz_meta_vectorDot<loopFlag * N, loopFlag * (I+1)>::f(a,b);
                }
                template<class T_expr1, class P_numtype2>
                static inline BZ_PROMOTE(_bz_typename T_expr1::T_numtype, P_numtype2)
                dotWithArgs(const T_expr1& a, P_numtype2 i1, P_numtype2 i2=0,
                            P_numtype2 i3=0, P_numtype2 i4=0, P_numtype2 i5=0, P_numtype2 i6=0,
                            P_numtype2 i7=0, P_numtype2 i8=0, P_numtype2 i9=0, P_numtype2 i10=0)
                {
                    return a[I] * i1 + _bz_meta_vectorDot<loopFlag * N, loopFlag * (I+1)>::dotWithArgs
                                                                                               (a, i2, i3, i4, i5, i6, i7, i8, i9);
                }
            };
            template<>
            class _bz_meta_vectorDot<0,0> {
            public:
                template<class T_expr1, class T_expr2>
                static inline _bz_meta_nullOperand f(const T_expr1&, const T_expr2&)
                { return _bz_meta_nullOperand(); }
                template<class T_expr1, class P_numtype2>
                static inline _bz_meta_nullOperand
                dotWithArgs(const T_expr1& a, P_numtype2 i1, P_numtype2 i2=0,
                            P_numtype2 i3=0, P_numtype2 i4=0, P_numtype2 i5=0, P_numtype2 i6=0,
                            P_numtype2 i7=0, P_numtype2 i8=0, P_numtype2 i9=0, P_numtype2 i10=0)
                {
                    return _bz_meta_nullOperand();
                }
            };
            這段代碼遠(yuǎn)比它乍看上去的簡單。_bz_meta_vectorDot類模板使用了一個(gè)臨時(shí)變量loopFlag來存放每一步循環(huán)條件的評(píng)估結(jié)果,并使用了一個(gè)完全特化版作為遞歸終結(jié)的條件。需要說明的是,和幾乎所有元程序一樣,這個(gè)臨時(shí)變量作用發(fā)揮于編譯期,并將從運(yùn)行代碼中優(yōu)化掉。  
            Todd是在Blitz++數(shù)值數(shù)組庫的主要作者。這個(gè)程序庫(以及MTL和POOMA等程序庫)例證了模板元程序可以為我們帶來更加高效的數(shù)值計(jì)算性能。Todd宣稱Blitz++的性能可以和對(duì)應(yīng)的Fortran程序庫媲美。  
            Loki程序庫:活用模板元編程技術(shù)的典范  
            模板元編程的價(jià)值僅僅在于高性能數(shù)值計(jì)算嗎?不僅如此。Loki程序庫以對(duì)泛型模式的開創(chuàng)性工作聞名于C++社群。它很巧妙地利用了模板元編程技術(shù)實(shí)現(xiàn)了Typelist組件。Typelist是實(shí)現(xiàn)Abstract Factory、Visitor等泛型模式不可或缺的基礎(chǔ)設(shè)施。  
            就像C++標(biāo)準(zhǔn)庫組件std::list提供對(duì)一組數(shù)值的操作一樣,Typelist可以用來操縱一組類型,其定義非常簡單(摘自Loki程序庫Typelist.h單元):  
            template <class T, class U>
            struct Typelist
            {
                typedef T Head;
                typedef U Tail;
            };
            顯然,Typelist沒有任何狀態(tài),也未定義任何操作,其作用只在于攜帶類型信息,它并未打算被實(shí)例化,因此,對(duì)于Typelist的任何處理都必然發(fā)生于編譯期而非運(yùn)行期。  
            Typelist可以被無限擴(kuò)展,因?yàn)槟0鍏?shù)可以是任何類型(包括該模板的其他具現(xiàn)體)。例如:  
            Typelist<char, Typelist<int, Typelist<float, NullType> > >   
            就是一個(gè)包含有char、int、float三種類型的Typelist。  
            按照Loki的約定,每一個(gè)Typelist都必須以NullType結(jié)尾。NullType的作用類似于傳統(tǒng)C字符串的“\0”,它被聲明于Loki程序庫的NullType.h文件中:  
            class NullType;  
            NullType只有聲明,沒有定義,因?yàn)長oki程序庫永遠(yuǎn)都不需要?jiǎng)?chuàng)建一個(gè)NullType對(duì)象。  
            讓我們看看IndexOf模板元程序,它可以在一個(gè)Typelist中查找給定類型的位置(摘自Loki程序庫的Typelist.h單元):  
            template <class TList, class T>
            struct IndexOf;
            template <class T>
            struct IndexOf<NullType, T>
            {
                enum { value = -1 };
            };
            template <class T, class Tail>
            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>::value };
            public:
                enum { value = (temp == -1 ? -1 : 1 + temp) };
            };
            IndexOf提供了一個(gè)原始模板和三個(gè)局部特化版。算法非常簡單:如果TList(就是一個(gè)Typelist)是一個(gè)NullType,則value為-1。如果TList的頭部就是T,則value為0。否則將IndexOf施行于TList的尾部和T,并將評(píng)估結(jié)果置于一個(gè)臨時(shí)變量temp中。如果temp為-1,則value為-1,否則value為1 + temp。  
            為了加深你對(duì)Typelist采用的模板元編程技術(shù)的認(rèn)識(shí),我從Loki程序庫剝離出如下代碼,放入一個(gè)typelistlite.h文件中:  
            // typelistlite.h
            // 聲明Nulltype
            class NullType;
            // Typelist的定義
            template <class T, class U>
            struct Typelist
            {
                typedef T Head;
                typedef U Tail;
            };
            // IndexOf的定義
            // IndexOf原始模板
            template <class TList, class T> struct IndexOf;
            // 針對(duì)NullType的局部特化版
            template <class T>
            struct IndexOf<NullType, T>
            {
                enum { value = -1 };
            };
            // 針對(duì)“TList頭部就是我們要查找的T”的局部特化版
            template <class T, class Tail>
            struct IndexOf<Typelist<T, Tail>, T>
            {
                enum { value = 0 };
            };
            // 處理TList尾部的局部特化版
            template <class Head, class Tail, class T>
            struct IndexOf<Typelist<Head, Tail>, T>
            {
            private:
                enum { temp = IndexOf<Tail, T>::value };
            public:
                enum { value = (temp == -1 ? -1 : 1 + temp) };
            };
            測試程序如下:  
            // typelistlite_test.cpp
            #include <iostream>
            #include "typelistlite.h"
            // 自定義類型Royal
            class Royal {};
            // 定義一個(gè)包含有char、int、Royal和float的Typelist
            typedef Typelist<char, Typelist<int, Typelist<Royal, Typelist<float, NullType> > > > CIRF;
            int main()
            {
                std::cout << "IndexOf<CIRF, int>::value = " << IndexOf<CIRF, int>::value << "\n";
                std::cout << "IndexOf<CIRF, Royal>::value = " << IndexOf<CIRF, Royal>::value << "\n";
                std::cout << "IndexOf<CIRF, double>::value = " << IndexOf<CIRF, double>::value << "\n";
            }
            程序輸出如下:  
            IndexOf<CIRF, int>::value = 1
            IndexOf<CIRF, Royal>::value = 2
            IndexOf<CIRF, double>::value = -1
            結(jié)語  
            模板元編程技術(shù)并非都是優(yōu)點(diǎn),比方說,模板元程序編譯耗時(shí),帶有模板元程序的程序生成的代碼尺寸要比普通程序的大,而且通常這種程序調(diào)試起來也比常規(guī)程序困難得多。另外,對(duì)于一些程序員來說,以類模板的方式描述算法也許有點(diǎn)抽象。  
            編譯耗時(shí)的代價(jià)換來的是卓越的運(yùn)行期性能。通常來說,一個(gè)有意義的程序的運(yùn)行次數(shù)(或服役時(shí)間)總是遠(yuǎn)遠(yuǎn)超過編譯次數(shù)(或編譯時(shí)間)。為程序的用戶帶來更好的體驗(yàn),或者為性能要求嚴(yán)格的數(shù)值計(jì)算換取更高的性能,值得程序員付出這樣的代價(jià)。  
            很難想象模板元編程技術(shù)會(huì)成為每一個(gè)普通程序員的日常工具,相反,就像Blitz++和Loki那樣,模板元程序幾乎總是應(yīng)該被封裝在一個(gè)程序庫的內(nèi)部。對(duì)于庫的用戶來說,它應(yīng)該是透明的。模板元程序可以(也應(yīng)該)用作常規(guī)模板代碼的內(nèi)核,為關(guān)鍵的算法實(shí)現(xiàn)更好的性能,或者為特別的目的實(shí)現(xiàn)特別的效果。  
            模板元編程技術(shù)首次正式亮相于Todd Veldhuizen的《Using C++ Template Metaprograms》論文之中。這篇文章首先發(fā)表于1995年5月的C++ Report期刊上,后來Stanley Lippman編輯《C++ Gems》一書時(shí)又收錄了它。參考文獻(xiàn)中給出了這篇文章的鏈接,它還描述了許多本文沒有描述到的內(nèi)容。  
            David Vandevoorde和Nicolai M. Josuttis合著的《C++ Templates: The Complete Guide》一書花了一整章的篇幅介紹模板元編程技術(shù),它同樣是本文的參考資料并且也應(yīng)該作為你的補(bǔ)充閱讀材料。
            Andrei Alexandrescu的天才著作《Modern C++ Design: Generic Programming and Design Patterns Applied》的第3章Typelists對(duì)Typelist有著更為詳盡的描述。
            參考文獻(xiàn)和資源
            1. David Vandevoorde, Nicolai M. Josuttis, C++ Templates: The Complete Guide, Addison Wesley, 2002.
            2.  Andrei Alexandrescu, Modern C++ Design: Generic Programming and Design Patterns Applied, Addison Wesley, 2001.
            3. 侯捷 於春景 譯,《C++設(shè)計(jì)新思維》,華中科技大學(xué)出版社,2003。
            4. Todd Veldhuizen, Template Metaprograms, http://osl.iu.edu/~tveldhui/papers/Template-Metaprograms/meta-art.html .
            5. Todd Veldhuizen, C++ templates as partial evaluation (PEPM99), http://osl.iu.edu/~tveldhui/papers/pepm99/.
            6. Erwin Unruh, Prime numbers(Primzahlen - Original), http://www.erwin-unruh.de/primorig.html .
            7. Erwin Unruh, Prime numbers(Primzahlen), http://www.erwin-unruh.de/Prim.html .
            8. Blitz++, http://www.oonumerics.org/blitz .
            9. Loki, http://sourceforge.net/projects/loki-lib .
            10. POOMA, http://www.pooma.com.
            11. MinGW - Minimalist GNU for Windows, http://sourceforge.net/projects/mingw.
            榮耀
            2003年10月
            南京師范大學(xué)
            www.royaloo.com

            posted on 2009-02-06 20:43 大龍 閱讀(280) 評(píng)論(0)  編輯 收藏 引用


            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            久久久噜噜噜久久熟女AA片| 免费精品久久久久久中文字幕| 中文字幕无码久久精品青草| 狠狠干狠狠久久| 亚洲欧洲久久av| 亚洲国产精久久久久久久| 国内精品久久久久国产盗摄| 99久久精品这里只有精品 | 99久久精品国产高清一区二区 | 中文字幕亚洲综合久久| 久久亚洲av无码精品浪潮| 亚洲国产欧洲综合997久久| 99久久精品免费观看国产| 久久亚洲AV成人无码| 欧美伊香蕉久久综合类网站| 9191精品国产免费久久| 亚洲国产另类久久久精品黑人 | 精品久久久久久久久午夜福利| 欧美亚洲另类久久综合| 久久久久亚洲国产| 欧美激情精品久久久久久久九九九 | 久久精品国产影库免费看| 欧美激情一区二区久久久| 欧美亚洲另类久久综合| 色综合久久久久综合体桃花网| 久久伊人亚洲AV无码网站| 国产精品久久久久久影院 | 久久伊人精品一区二区三区| 亚洲级αV无码毛片久久精品| 久久嫩草影院免费看夜色| 久久精品国产精品亚洲精品| 日本欧美国产精品第一页久久| 久久国产亚洲精品麻豆| 久久婷婷激情综合色综合俺也去| 久久青青草原精品国产软件| 久久久久久亚洲精品成人| 久久丫精品国产亚洲av| 99精品国产99久久久久久97| 久久国产影院| 四虎国产精品成人免费久久| 99久久综合狠狠综合久久|