為了讓更高級(jí)的語言可以編譯到
Vczh Library++ 3.0上面的NativeX語言,原生的泛型支持是必須有的。泛型不僅僅是一堆代碼的填空題那么簡(jiǎn)單,因?yàn)榫幾g之后的Assembly(dll)必須可以容納泛型聲明,而且其他的Assembly可以實(shí)例化包含在其他Assembly里面的泛型聲明。這是非常麻煩的(被.net搞定了,jvm則由于種種原因搞不定,大概是因?yàn)閖vm對(duì)assembly version的支持太差導(dǎo)致的,你知道.net 2.0的東西是不能引用4.0的dll的……)。不過先拋開這個(gè)不講,雖然如何在Assembly里面實(shí)現(xiàn)泛型我已經(jīng)心里有數(shù)了,但是這里還是從語義的層面上來考慮泛型的設(shè)計(jì)。
在討論之前還是要強(qiáng)調(diào)一下一個(gè)大前提:
NativeX基本上就是一個(gè)語法更加容易看懂的C語言而已,功能完全是等價(jià)的。于是我要在NativeX上加泛型,其實(shí)也就是等于在C上面加泛型。我們使用泛型完成的事情可以有很多,譬如說定義泛型的結(jié)構(gòu)體,定義泛型的函數(shù),還有泛型的存儲(chǔ)空間等等。首先讓我們討論泛型的結(jié)構(gòu)體。最終的語法可能會(huì)跟現(xiàn)在不一樣,因?yàn)镹ativeX的使命是作為一棵語法樹出現(xiàn)的,所以做得太漂亮的價(jià)值其實(shí)不是很大。
一、泛型結(jié)構(gòu)體 泛型的結(jié)構(gòu)體還是比較容易理解的,舉個(gè)小例子:
1 generic<type T>
2 structure Vector
3 {
4 T x;
5 T y;
6 }
這樣子我們就創(chuàng)建了一個(gè)泛型的結(jié)構(gòu)體。任何熟悉C++或C#的人都知道這是什么意思,我就不做解釋了。使用的時(shí)候跟我們的習(xí)慣是一樣的:
1 Vector<double> v;
2 v.x = 1.0;
3 v.y = 2.0;
于是我們創(chuàng)建了一個(gè)泛型的變量,然后修改了它的成員。
二、泛型全局存儲(chǔ)空間 其實(shí)泛型的全局存儲(chǔ)空間基本上等于編譯器替你做好的一個(gè)key為類型的大字典。有些時(shí)候我們需要給類型加上一些附加的數(shù)據(jù),而且是按需增長的。這就代表在編譯的時(shí)候提供泛型全局存儲(chǔ)空間的Assembly并不知道將來有多少個(gè)key要提供支持,所以創(chuàng)建它們的工作應(yīng)該是虛擬機(jī)在鏈接一個(gè)想使用其他Assembly提供的全局空間的新Assembly的時(shí)候創(chuàng)建的。這里帶來了一個(gè)問題是不同的Assembly使用相同的類型可以訪問相同的全局存儲(chǔ)空間,這里先不討論具體實(shí)施的手段。
語法上可能比較混淆:
1 generic<type T>
2 structure TypeStorage
3 {
4 wchar* name;
5 function T() builderFunction;//這是函數(shù)指針
6 }
7
8 generic<type T>
9 TypeStorage<T> typeStorage;
在一個(gè)變量上面加泛型可能會(huì)有點(diǎn)奇怪,不過這里的定義還是很明確的。typeStorage是全局變量的泛型,因此typeStorage<int>、typeStorage<double>甚至typeStorage<Vector<Vector<wchar*>>>等等(啊,>>問題)是代表不同的全局變量。不同的Assembly訪問的typeStorage<int>都是相同的全局變量。
三、泛型函數(shù) 泛型函數(shù)我們也都很熟悉了。一個(gè)簡(jiǎn)單的例子可以是:
1 generic<type T>
2 T Copy(T* pt)
3 {
4 result = *pt;
5 }
需要指出的是,NativeX并沒有打算要支持泛型結(jié)構(gòu)、全局存儲(chǔ)和函數(shù)的特化或偏特化。因此我們會(huì)很驚訝的發(fā)現(xiàn)這樣的話泛型函數(shù)唯一能做的就是復(fù)制東西了,因?yàn)樗{(diào)用不了其他的非泛型函數(shù)(跟C++不一樣,NativeX的泛型函數(shù)更接近于C#:編譯的時(shí)候進(jìn)行完全的語義分析)。雖然泛型函數(shù)可以調(diào)用其他的泛型函數(shù),但是最終也只能做復(fù)制。因此我們要引入一個(gè)新的(其實(shí)是舊的)概念,才可以避免我們?yōu)榱颂峁└鞣N操作在泛型函數(shù)的參數(shù)上傳入一大堆的函數(shù)指針:“概念”。
四、泛型concept 泛型結(jié)構(gòu)體和全局存儲(chǔ)僅僅用來保存數(shù)據(jù),所以泛型concept只能在泛型函數(shù)上面使用。這個(gè)concept跟C++原本打算支持的concept還是很接近的,只是NativeX沒有class,因此只好做一點(diǎn)小修改。
泛型concept主要是為了指定一套操作的接口,然后讓編譯器可以完成比調(diào)用函數(shù)指針更加高效的代碼。偷偷告訴大家,把他叫concept只是因?yàn)镹ativeX跟C很像,但其實(shí)這個(gè)概念是從Haskell來的……我們還是來看看concept怎么寫吧。
1 generic<type T>
2 concept Addable
3 {
4 operation T add(T a, T b);
5 operation T sub(T a, T b);
6 constant T zero;
7 }
8
9 generic<type T>
10 concept Multible : Addable<T>
11 {
12 operation T mul(T a, T b);
13 operation T div(T a, T b);
14 constant T one;
15 }
這里定義了加法和乘法的兩個(gè)concept。我們可以看出concept是可以繼承的,其實(shí)也是可以多重繼承的。concept里面可以放操作(operation),也可以放常數(shù)(constant)。這里的常數(shù)跟全局存儲(chǔ)的機(jī)制不同,全局存儲(chǔ)可以自動(dòng)為新類型產(chǎn)生可讀寫的空間,而concept的常數(shù)不僅是只讀的,而且還不可自動(dòng)產(chǎn)生空間。之前考慮到的一個(gè)問題就是,我們可能需要把外界提供的某個(gè)concept的operation的函數(shù)指針提取出來,有這種需要的operation可以把這個(gè)關(guān)鍵字替換成function,這樣在實(shí)例化concept的時(shí)候,那個(gè)標(biāo)記了function的操作就只能綁定一個(gè)函數(shù)而不是一個(gè)表達(dá)式了。我們可以嘗試為int創(chuàng)建一個(gè)Multible的concept:
1 concept instance IntMultible : Multible<int>
2 {
3 operation T add(T a, T b) = a+b;
4 operation T sub(T a, T b) = a-b;
5 operation T mul(T a, T b) = a*b;
6 operation T div(T a, T b) = a/b;
7 constant T zero = 0;
8 constant T one = 1;
9 }
于是我們可以寫一個(gè)函數(shù)計(jì)算a+b*c:
1 generic<type T, concept Multible<T> multible>
2 function T AddMul(T a, T b, T c)
3 {
4 return multible.add(a, multible.mul(b, c));
5 }
然后調(diào)用它:
1 int r = AddMul<int, IntMultible>(3, 4, 5);
五、另一種concept instance 雖然我們不允許泛型的結(jié)構(gòu)體、全局存儲(chǔ)和函數(shù)進(jìn)行特化,但是因?yàn)樘鼗瘜?shí)在是一個(gè)好東西。上面的concept instance是沒有彈性的,因?yàn)槟悴豢赡芡ㄟ^一個(gè)concept instance拿到另外一個(gè)concept instance。考慮一下delphi的帶引用計(jì)數(shù)的嵌套數(shù)組,如果我們想讓delphi可以編譯到NativeX上,則勢(shì)必要支持那種東西。主要的困難在于delphi支持的帶有引用計(jì)數(shù)的數(shù)組和字符串,因此在對(duì)array of array of string進(jìn)行釋放的時(shí)候,我們首先要拿到array of array of string的concept instance,其次在釋放函數(shù)里面要拿到array of string的concept instance,最后還要拿到string的concept instance。這個(gè)用上面所提出來的方法是做不了的。因此我們引進(jìn)了一種新的concept instance:叫concept series。這個(gè)跟haskell的東西又更接近了一步了,因?yàn)閔askell的concept instance其實(shí)是匿名但是可特化的……
于是現(xiàn)在讓我們來實(shí)現(xiàn)Array和String,并寫幾個(gè)類型的Increase和Decrease的函數(shù)(函數(shù)體一部分會(huì)被忽略因?yàn)檫@里只是為了展示concept):
1 structure String
2 {
3 int reference;
4 wchar* content;
5 }
6
7 generic<type T>
8 structure Array
9 {
10 int reference;
11 int length;
12 T* items;
13 }
我們從這里可以看出,string跟array的區(qū)別就是在于長度上面,string有0結(jié)尾而array只能通過記錄一個(gè)長度來實(shí)現(xiàn)。現(xiàn)在我們來寫一個(gè)用于構(gòu)造缺省數(shù)值、增加引用計(jì)數(shù)和減少引用計(jì)數(shù)的concept series:
1 generic<type T>
2 concept Referable
3 {
4 operation T GetDefault();
5 operation void Increase(T* t);
6 operation void Decrease(T* t);
7 }
8
9 generic<type T>
10 concept series DelphiTypeReferable : Referable<T>
11 {
12 }
concept series其實(shí)就是專門用來特化的concept instance。但是為了防止不同的Assembly特化出同一個(gè)concept series所帶來的麻煩,我可能會(huì)規(guī)定允許特化concept series的地方,要么是在聲明該concept series的Assembly,要么是聲明涉及的類型的Assembly。因?yàn)槲业腁ssembly不允許循環(huán)引用,因此對(duì)于同一個(gè)concept series C<T,U>來講,就算T和U分別在不同的Assembly出現(xiàn),那么也只能有一個(gè)有權(quán)限特化出它。下面來看特化具體要怎么做。首先我們特化一個(gè)簡(jiǎn)單的,string的DelphiTypeReferable:
1 concept series DelphiTypeReferable<String>
2 {
3 operation GetDefault = StringGetDefault;
4 operation Increase = StringIncrease;
5 operation Decrease = StringDecrease;
6 }
StringGetDefault、StringIncrease和StringDecrease都是一些普通的函數(shù),內(nèi)容很簡(jiǎn)單,不用寫出來。現(xiàn)在讓我們來看看Array應(yīng)該怎么做:
1 generic<type T>
2 concept series DelphiTypeReferable<Array<T>>
3 {
4 operation Array<T> GetDefault() = ArrayGetDefault<T>;
5 operation Increase = ArrayIncrease<T>;
6 operation Decrease = ArrayDecrease<T>;
7 }
看起來好像沒什么特別,不過只要想一想ArrayDecrease的實(shí)現(xiàn)就知道了,現(xiàn)在我們需要在ArrayDecrease里面訪問到未知類型T的DelphiTypeReferable<T>這個(gè)concept instance。因?yàn)楫?dāng)自己要被干掉的時(shí)候,得將引用到的所有對(duì)象的引用計(jì)數(shù)都減少1:
1 generic<type T>
2 function void ArrayDecrease(Array<T>* array)
3 {
4 if(array->reference<=0)exit;
5 if(--array->reference==0)
6 {
7 for int i = 0
8 when i < array->length
9 with i--
10 do DelphiTypeReferable<T>.Decrease(&array->items[i]);
11 free(array->items);
12 array->length=-1;
13 array->items=null;
14 }
15 }
這樣一大堆concept series的特化組合在一起就成為會(huì)根據(jù)類型的變化而采取不同行為的concept instance了。于是我們還剩下最后的一個(gè)問題,那么其他類型的DelphiTypeReferable應(yīng)該怎么寫呢?其實(shí)只需要玩一個(gè)小技巧就行了,不過在這里將會(huì)看到NativeX支持泛型的最后一個(gè)功能:
1 generic<type T>
2 concept series DelphiTypeReferable<T>
3 {
4 operation GetDefault = GenericGetDefault<T>;
5 operation Increase = null;
6 operation Decrease = null;
7 }
8
9 generic<type T>
10 T GenericGetDefault()
11 {
12 }
返回null的operation可以賦值成null以表示不需要執(zhí)行任何東西。如果你將一個(gè)有副作用的表達(dá)式傳進(jìn)去當(dāng)參數(shù)的話,副作用會(huì)保證被執(zhí)行。
關(guān)于語義上的泛型就講到這里了。
posted on 2010-06-12 23:58
陳梓瀚(vczh) 閱讀(2532)
評(píng)論(2) 編輯 收藏 引用 所屬分類:
VL++3.0開發(fā)紀(jì)事