大概都是要定稿了罷,想了這么久……前人果然是前人啊,C++的concept也好,Haskell的type class也好,C#的generic interface也好,都非常精確地描述出了NativeX的泛型所應該有的形式。設計語言什么的,還是大部分要抄啊……
接
上一篇文章。昨天晚上
Vczh Library++的泛型結構體以及泛型類型重命名已經搞定了。這部分先做是因為泛型結構體以及泛型類型重命名都不需要在鏈接的時候產生新的指令表,因此完全是編譯器的事情,不需要修改虛擬機。先來看看泛型結構體以及泛型類型重命名的樣子。這次我仍然在單元測試用例里面生成了一個語法樹,然后反編譯成NativeX代碼,然后再一次編譯成語法樹,最后給生成的指令自動加注釋:
1 /*NativeX Code*/
2 unit nativex_program_generated;
3 generic<T>
4 type Unit = T;
5
6 generic<T>
7 structure Vector
8 {
9 Unit<T> x;
10 Unit<T> y;
11 }
12
13 function Unit<int32> main()
14 {
15 variable Unit<Vector<int32>> v;
16 (v.x=10);
17 (v.y=20);
18 (result=(v.x+v.y));
19 }
20
21
22 /*Assembly*/
23 .data
24 .label
25 0: instruction 3
26 .code
27 // unit nativex_program_generated;
28 0: stack_reserve 0
29 1: stack_reserve 0
30 2: ret 0
31 // function Unit<int32> main()
32 3: stack_reserve 8
33 // (v.x=10);
34 4: push s8 10
35 5: convert s32 s8
36 6: stack_offset -8
37 7: push s32 0
38 8: add s32
39 9: write s32
40 // (v.y=20);
41 10: push s8 20
42 11: convert s32 s8
43 12: stack_offset -8
44 13: push s32 4
45 14: add s32
46 15: write s32
47 // (result=(v.x+v.y));
48 16: stack_offset -8
49 17: push s32 4
50 18: add s32
51 19: read s32
52 20: stack_offset -8
53 21: push s32 0
54 22: add s32
55 23: read s32
56 24: add s32
57 25: resptr
58 26: write s32
59 // function Unit<int32> main()
60 27: stack_reserve -8
61 28: ret 0
62
在這里可以看出實際上編譯完了之后,指令集里面根本不會包含有關反省的任何信息,甚至是原先的類型也都丟掉了。當然為了解決這個問題,我給Assembly加了“資源”,那是一種通過C++的技巧封裝之后,你可以不斷地構造越來越大的只讀數據結構,方便二進制形式的序列化和反序列化。所有的信息都存在里面,供以后使用(反正虛擬機不需要讀)。
但是泛型的全局變量、函數和契約就不一樣了。泛型全局變量還是很容易做的因此我就忽略掉了。泛型的函數需要把契約的類型完整保留在指令表里面,這樣在特化的時候才知道哪些地方需要被替換掉。總的來說最終的設計是這個樣子的:
首先是契約,跟上次差不多,只是命名契約被我刪除了,只剩下匿名契約。總的來說我只需要在鏈接的時候進行檢查就好了,如果發現新來的Assembly重復實現了舊Assembly已經特化過的一個契約,那就會出現鏈接錯誤。至于特化要實現在哪里,我就不在編譯器上座約束了,因為這個代價更大,而且約束了靈活性。
其次是函數。函數的泛型頭現在被我修改成了:
1 generic<T>
2 concept Comparable
3 {
4 int Compare(T a, T b);
5 }
6
7 generic<T> with
8 Comparable<T> ct
9 function bool AreEqual(T a, T b)
10 {
11 result = ct::Compare(a, b)==0;
12 }
你會發現最終concept變成了對一個類型或者一組類型附加的屬性。泛型的函數除了這些屬性以外,就只能用一些基本的東西了(當然如果你把一個變量T的地址拿出來,強轉……)。這些時候所有泛型參數類型的參數、變量和結構體的地址都變成了一個表達式,譬如說&a == stack_offset+sizeof(int)*4而&b == stack_offset+sizeof(int)*4+sizeof(T)等等。而且如果AreEqual要調用其它關于T的泛型函數的話,如果其他的泛型函數對concept的要求比Comparable更多,那么就變成了編譯錯誤。當然最簡單的解決辦法就是在AreEqual函數上把所有用到的concept全部加滿。
當然,最后一個泛型函數還是可以被編譯成指令表和一組待計算向量的,只是鏈接的時候,會查看新來的Assembly需要多少還沒特化的函數,然后一一為他們生成。于是現在最難的問題就變成了重構已有代碼,以及如何判斷concept instance是否被多個Assembly重復特化了……
posted on 2010-06-19 00:07
陳梓瀚(vczh) 閱讀(2427)
評論(3) 編輯 收藏 引用 所屬分類:
VL++3.0開發紀事