大概都是要定稿了罢,想了这么久……前人果然是前人啊,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) 阅读(2416)
评论(3) 编辑 收藏 引用 所属分类:
VL++3.0开发纪事