随笔-341  评论-2670  文章-0  trackbacks-0
 
    之前的一篇文章曾经说过我在准备一个面向组合子编程的ppt和demo。在公司内部进行试运行之后,今天终于开讲了。

    当然在这之间的几个星期里面,我修改了ppt的内容,还有为demo添加了单元测试。所以我把ppt的内容和demo的代码也一起打包上来了。

    ppt包含2003和2007两种格式,demo使用的是Visual Studio2010的C#,当然是为了使用那个激动人心的“缺省参数”语法了。如果没装2010的话,那就赶紧装一个哈,如果不想装,那看看代码就行了。
posted @ 2010-09-01 20:42 陈梓瀚(vczh) 阅读(5144) | 评论 (3)编辑 收藏
    因为在开发CMinus的过程中为了异常处理(最终没有实现进CMinus),曾经学习了一下怎么用汇编语言写try-catch,因此这个CPU相关的处理方法就被我偷了哈,实现在了NativeX的虚拟机里。

    在NativeX里面,try-catch和throw非常简单。throw你可以加一个值当异常数据,也可以不加(不会修改上次的异常数据,可以当rethrow用)。catch的话没办法跟C++一样根据类型来判断,因此我会给你一个异常数据的指针,你自己看着办哈,因为NativeX跟C一样没有RTTI。因此throw就很简单了,就是恢复栈顶和栈底指针之后跳转到最近的异常处理程序里面去。try和catch就是用来创建和销毁异常处理程序的。所有的异常处理程序构成了一个链表,这个链表被我记在了堆栈里面,而最近的异常处理节点的指针则被我放在了整个堆栈控件的最顶部,接在后面的是异常对象的数据。你每次throw的东西的尺寸可以不同,因此占用的“堆栈最顶部空间”也不同。当然如果你函数递归太深而导致栈顶覆盖了异常对象的数据区域时,就会触发“堆栈溢出”事件。在NativeX里面堆栈溢出代表你这程序已经废了,因此这个是不能catch的,虚拟机返回给宿主程序一个信号然后就停止执行了。

    我们来看一个简单的例子,如何throw之后把异常对象的返回给函数,首先是代码:
 1 /*NativeX Code*/
 2 unit nativex_program_generated;
 3 function int32 main()
 4 {
 5     (result = 10s32);
 6     try
 7         Throw();
 8     catch
 9         (result = ( * cast<int32*>(exception)));
10 }
11 
12 function void Throw()
13     throw 20s32;

    main函数首先将函数返回值设置成10,然后调用throw函数。throw函数会把20给throw出来,然后main函数catch了,把结果返回。NativeX使用了关键字exception来表达异常对象的地址。当然你如果要throw各种不同的东西的话,你得自己做标记(亲自实现RTTI)了。好了,我们看看产生的指令:
 1 // unit nativex_program_generated;
 2      0: stack_reserve 0
 3      1: stack_reserve 0
 4      2: ret 0
 5 // function int32 main()
 6      3: stack_reserve 0
 7 // (result = 10s32);
 8      4: push s32 10
 9      5: resptr
10      6: write s32
11 // try
12      7: exception_handler_push 14
13 // Throw();
14      8: stack_reserve 1
15      9: stack_top 0
16     10: call 20 1
17     11: stack_reserve -1
18 // try
19     12: exception_handler_pop
20     13: jump 18 1
21     14: exception_handler_pop
22 // (result = ( * cast<int32*>(exception)));
23     15: exception_object_address
24     16: resptr
25     17: copymem 4
26 // function int32 main()
27     18: stack_reserve 0
28     19: ret 0
29 // function void Throw()
30     20: stack_reserve 0
31 // throw 20s32;
32     21: exception_object_reserve 4
33     22: push s32 20
34     23: exception_object_address
35     24: write s32
36     25: exception_raise
37 // function void Throw()
38     26: stack_reserve 0
39     27: ret 0

    try首先会将catch之后的第一个指令给exception_handler_push了,在try的结尾当然要取消掉这个异常处理函数了,因此pop一下,然后jump到catch后面。当然catch的第一件事也是exception_handler_pop。exception_object_reserve在栈顶预留指定的空间来存放异常对象,exception_object_address则是获得异常对象的地址,exception_raise就是跳转到最近的异常处理函数了。raise不会把异常处理函数的记录给pop掉,所以要靠catch自己去pop。

    NativeX已经完成了,接下来就可以开始打造周边工具了哇哈哈。将来的目标是将类似C#和Javascript的语言都编译到NativeX上,然后为这三类语言写很多语法分析器,然后他们就变成很多语言了。当然这些语言只是demo。Vczh Library++的目的是提供实现编译器的中间每一层的类库,因此想干嘛就可以干嘛了哈。
posted @ 2010-08-28 01:14 陈梓瀚(vczh) 阅读(3463) | 评论 (9)编辑 收藏
     摘要:     如上一篇文章所说,Vczh Library++3.0的NativeX语言实现了计划的所有泛型语法。让我们来看一个简单的例子:我们为类型写一个Equals函数。我们可以为普通类型,譬如int32写一个Equals函数。我们有Vector<T>类型,只要T类型拥有一个Equals函数,那么Vector<T>显然也可以有Equals函数。问题...  阅读全文
posted @ 2010-08-26 03:09 陈梓瀚(vczh) 阅读(3075) | 评论 (3)编辑 收藏
    这几天写了一个关于面向组合子编程的ppt。几个月前跟某个dev lead借了他翻译的中文版Pattern Hatching,条件是要在公司里面开一场关于设计模式的讲座。其实本来一个月前就要讲了,不过中间出了点事情,所以等到这个月才开始。因此我挑选了面向组合子编程的这个主题,做了个demo和ppt。

    面向组合子编程原本是函数式编程的内容,主要说的是既然我们可以用Composite模式(参见这里这里)来做出像树一样的数据结构,那么我们做出组合起来跟树一样的行为(譬如Command模式,用类代表行为)不也可以吗?这个做法当然是行得通的,只不过一般我们很难看到一个需求的时候,可以意识到可以用面向组合子编程来搞定这个东西。因此我在这个ppt里面就举了这样的一个例子,也就是老掉牙的Log系统了:

    我们编译器在编译代码的时候,会产生下面的文件:
    buildchk.err -- 记录着错误
    buildchk.wrn -- 记录着警告
    buildchk.log -- 记录所有详细信息和时间戳
    命令行窗口 -- 记录摘要,当然错误和警告还是要输出来的,只是内容可以简要一点

    然后我就用面像组合子来开发了一个小巧玲珑的系统,最终通过一个声明式编程的接口暴露出来,然后你还可以往里面添加新的功能。

    当然系统还是要经得起修改的,因此我还举了个例子,如果有了需求变更——
    err和wrn要加错误/警告的序号
    支持GUI了——跟VS的错误列表差不多

    最后展示了面向组合子编程最强大的威力——只需要添加零件,所有已经存在的工具都可以立刻在这个零件上面使用了,因此只需要非常少的代码就可以完成这个需求变更。

    这里就放上我的ppt了。明天还要先开一个内部讲座看看别人有什么意见然后进一步修改,完了demo在放出来。这个demo当然是C#写的了,有GUI,C++写GUI多麻烦啊……
posted @ 2010-08-16 09:10 陈梓瀚(vczh) 阅读(4871) | 评论 (15)编辑 收藏
    最近在忙一些其他的事情。因为工作的关系我稍微花了点时间研究了一下C#,因此就没往博客上写文章了。Vczh Library++ 3.0的工作也暂停了半个月,下个星期就要开始恢复了。最近Codeplex服务器的URL修改了导致项目连接不上,后来还是修掉了,主要是得手动更改sln文件的内容,花了好久才知道怎么做。

    目前的进度是实现了generic constraint的数据结构但是没有加入语法分析和语义分析的内容。generic constraint比较简单,就如同C#的那个where,我可以写:
 1 concept T : IEq
 2 {
 3   
 4 }
 5 
 6 concept T : ISort
 7   where T : IEq
 8 {
 9   
10 }
11 
12 generic<T>
13 where T : ISort
14 function void Sort(T* values, int count)
15 {
16   
17 }

    这是很重要的,因为没有了where,在Sort下面就没办法使用ISort和IEq里面定义的函数了。在NativeX可以成为一门真正可以使用的中间语言之前,还必须实现下面的功能:

   1、 generic constraint
    2、concept instance函数调用
    3、异常处理
    4、外部函数接口
    5、调试器接口
    6、装载的时候检查元数据引入表是否匹配了所有已经加载的assembly

    剩下的事情也不多了,就慢慢做吧。做完之后就可以开始写一些parser来验证这个NativeX究竟行不行了,我可以将Python和Basic都修改成一个类似C的语言(可以处理指针,没有垃圾收集,等),然后把它编译成NativeX的语法树,这样就可以开发一个支持多语言的编程接口并测试它了。
posted @ 2010-08-06 18:31 陈梓瀚(vczh) 阅读(3144) | 评论 (12)编辑 收藏
     摘要:     根据之前的文章的讨论,Vczh Library++3.0泛型全局存储是一个大型的映射。假设你有下面的代码: 1 generic<T>2 structure StorageType3 {4   wchar* name;5   T ...  阅读全文
posted @ 2010-07-17 21:28 陈梓瀚(vczh) 阅读(2939) | 评论 (2)编辑 收藏
    第三篇草稿讲了泛型concept的概念,这篇最终稿可以确定Vczh Library++ 3.0的NativeX所要支持的泛型concept的比较精确的特性了。

    泛型的concept的概念还是比较清晰的,这次我们便通过一个例子来讲解他。假如在NativeX里面实现一个列表,显然因为NativeX只有指针,所以写起来大概就是:
1 generic<T>
2 structure List
3 {
4   T* items;
5   int count;
6 }

    于是我们想写一个函数判断两个List是否相等。因为NativeX现在已经有模板函数了,所以我们很简单的一个想法是,传入两个List<T>*,然后再传入一个函数指针function bool(T,T),就可以写出下面的函数了:
 1 generic<T>
 2 function bool ListEquals(List<T>* xs, List<T>* ys, function bool(T,T) comparer)
 3 {
 4   result=true;
 5   if(xs->count!=yx->count)
 6   {
 7     result=false;
 8   }
 9   else
10   {
11     variable int current=xs->count-1;
12     while(current>=0)
13     {
14       if(!comparer(xs->items[current], yx->items[current]))
15       {
16         result=false;
17         exit;
18       }
19       current--;
20     }
21   }
22 }

    这个做法咋一看好像没什么问题,但是如果我们要比较List<List<int>>怎么办呢?我们可以先写一个function bool IntEquals(int a, int b);,然后再写一个function bool IntListEquals(List<T> a, List<T> b);,这个函数里面用IntEquals加上ListEquals<int>来实现,最后将他传进ListEquals<List<int>>。到了这里还好,如果我们还想比较List<List<double>>、List<List<char>>等等,我们就要被迫搞出很多函数了。而且最大的问题是,当我们写好这么多东西以后,我们想实现一个Find函数来查找List里面的对象的话,面对List<List<int>>、List<List<double>>等等的东西,我们又得重新写一次了……

    这个问题在面向对象的语言里面都很容易做到,只需要在一个类里面实现operator==就可以了。那这两种方法有什么区别呢?假设我们把C++里面的类去掉,那么我们会发现,ListEquals<T>的comparer参数是通过T自动找到的,而不是我们自己传进去的。因此如何设计一套可以从类型找到某一组函数的机制,就因为我们把模板函数加入NativeX语言(基本上就是C语言)而变成了一个必须实现的功能了。在这里我准备引入concept这个概念。concept跟C++0x的concept以及haskell的type class的概念基本一致。现在就让我们逐步在NativeX里面实现这套机制。

    我们在这里面对的问题是给一些给定的类型(或泛型类型,譬如说List<T>)实现Equals函数,然后每一个Equals函数里面如果需要其他类型U的Equals函数,可以直接找到,而不需要我们自己传进去。因此这个问题可以抽象为,某些类型具有可以被两两比较是否相等的能力。因此定义如下:
1 generic<T>
2 concept Eq
3 {
4   bool Equals(T a, T b);
5 }

    当然这些函数不能直接生出来,因此我们对于想比较的每一个类型都需要给出相应的Equals函数。下面的代码为int类型定义了Equals:
 1 instance int : Eq
 2 {
 3   Equals = IntEquals;
 4 }
 5 
 6 generic<T>
 7 function bool IntEquals(int a, int b)
 8 {
 9   result=a==b;
10 }

    每比较一次数字就得调用一次函数,这个开销还是比较大的。在NativeX的所有设施都做好之后,我会开始做指令级别的优化,然后看看要不要实现自动判断并inline的功能。有了int : Eq之后,我们就可以为List<T>也写一个Eq了。如果我们给出了List<T>的Equals的话,为了使用这个Equals,T也必须有相应的Equals函数,于是我们可以写:
1 generic<T>
2 instance List : Eq
3   where T : Eq
4 {
5   Equals = ListEquals<T>;
6 }

    最后就是实现ListEquals了,注意ListEquals函数内部是如何拿到类型T的Equals函数的:
 1 generic<T>
 2   where T : Eq
 3 function bool ListEquals(List<T> xs, List<T> ys)
 4 {
 5   result=true;
 6   if(xs->count!=ys->count)
 7   {
 8     result=false;
 9   }
10   else
11   {
12      variable int current=xs->count-1;
13      while(current>=0)
14      {
15        if(!Eq<T>::Equals(xs->items[current], yx->items[current]))
16        {
17          result=false;
18          exit;
19        }
20        current--;
21      }
22   }
23 }

    这里引入了一个新的语法:Eq<T>::Equals,用于自动搜索自己dll或者其他dll实现的这个函数。搜索会在虚拟机里面完成,编译器只负责提供符号,并检查类型。因此就大功告成了。

    这个instance的设计基本上来源于Haskell的type class,其实跟C++0x的那个concept关系还是比较小,毕竟NativeX没有类,C++有类,Haskell没有类。当然其缺点是你不能在定义了Eq<List<T>>::Equals的同时,专门为Eq<List<bool>>::Equals定义一个特殊的版本,就如同C++的偏特化一样,这个就过于复杂了。虽然偏特化在C++的用处非常大,而且也十分常用,但是在NativeX里面就因为NativeX的模板可以编译成二进制而会因为找不到高性能的实现方法被砍掉。
posted @ 2010-07-13 04:26 陈梓瀚(vczh) 阅读(2978) | 评论 (8)编辑 收藏
     摘要:     经过一个星期的奋斗,二进制模板函数终于实现了,当然这还是没有generic concept的版本。现在NativeX已经支持跟C#一样的模板函数了:可以被编译进独立的二进制文件,然后另外一个代码引用该二进制文件,还能实例化新的模板函数。现在先来看debug log输出的二进制结构。首先是被编译的代码。下面的代码因为是直接从语法树生成的,所以括号什么的会比较多,...  阅读全文
posted @ 2010-07-12 03:12 陈梓瀚(vczh) 阅读(3034) | 评论 (8)编辑 收藏
    似乎C++“过于复杂”已经成为了诟病,不过对于我个人来讲我实在很难理解这个观点。之前有个朋友说stream::operator<<很复杂,其实也就是几个overloading。还有些人说传参数的时候很复杂,这无非就是复制构造函数、析构函数和引用吧。虽然我个人觉得模板元编程其实才是C++里面最复杂的地方,但是鉴于模板元编程实际的用处不大,我想应该只有少数几个人会使用它。但是这样很多人还是C++复杂,那我就不知道究竟在指什么了。

    所以大家对C++有什么想喷的就赶紧留言哈,我也好看看别人是怎么理解的,然后讨论讨论。

    (不过从我自己的角度出发,我认为凡是编译器不能检查的东西(譬如可变参数,指针类型强制转换),都远比能检查的东西(模板元编程)要复杂,因为人很容易犯错,机器不会。)
posted @ 2010-07-06 19:52 陈梓瀚(vczh) 阅读(11404) | 评论 (68)编辑 收藏
    这年来屡屡被网友教育说不要造车轮,我觉得我有必要专门写几句话来阐述我的观点。

    1:公司的代码,自然有规定,你造不了车轮。
    2:自己外包赚钱的代码,造了也只会浪费时间,这个随便你。
    3:自己写的代码。无论你开源也好,不开源也好,自己写那些不能换钱的代码无非就是因为你写的爽嘛,那造车轮还能提高自己功力,为啥总是有人来说这样不行呢?这又不是公司的代码,也不是拿去完成别人外包给我项目的代码,这个时候你还用别人的东西,完全是没有意义的。除非你所谓的学习就是学习如何使用别人的车轮。当然我自己的定义是,学习造车轮,不仅能知道很多你不造车轮不知道的东西,同时造完了,你看别人的车轮,瞬间就知道怎么用了。而且如果你想的话,你还能研究一下怎么比别人造得更好。什么?你相信自己无论如何这一辈子写到死也比别人烂么?那就是另一回事了。
posted @ 2010-07-01 01:14 陈梓瀚(vczh) 阅读(5816) | 评论 (21)编辑 收藏
仅列出标题
共35页: First 9 10 11 12 13 14 15 16 17 Last