KeyFansClub

首页 » - 特色讨论区 - » 键社茶餐厅 » [M] Prelude to K.O. (4.5) + 12楼(4.75)
Prz - 2007/6/2 14:47:00
K.O.(KeyFC Open Translation Toolset) 是目前正在开发的“开放版本”汉化工具的代号。
其中的汉化部分工具试验性的采用了代号为Chobits Application Server(CAS)的架构。
这个结构的特点为,程序全部由基于消息传递的模块动态组合而成。

前面的四回把架构的关键特征进行了抽象的描述,本来不打算写第五回的(因此这回叫4.5),但是...
在过去的一个多星期我埋头苦干,将CAS代码从头到尾的梳理了一遍。因此KO部分的工作暂停了下来... XD

更改的地方比较的多:很多功能被规范化,无数细小的错误(和潜在错误)得到了更正,部分核心代码简化... (还好以前写得算整齐,不然就晕了... =v=)
尽管总的架构没有太大改变,但是我觉得还是有必要作一下文档工作,避免以后自己忘了 :D

主要的更改点有:
1. CAS消息扩展功能的规范化
2. 线程和线程池的包装完善化
3. 意外对象(Exception Object)的完善化


--- CAS 消息 ---

听说Delphi 2007的Together更加的稳定,于是我也开始学着玩玩,发现这东西确实不错 =v=
Model支持与IDE完美的集成,只要几点鼠标,20秒下面的图就出来了 :P
照着讲...



* 增加BusMSG接口。
话说本次修订前,其实CAS总线并没有真的支持完全自定义的消息实例,因为发送消息的方法接受的是以BaseMSG为基类的对象... :P
后来我在一边讲的时候意识到了这一点,于是现在添加一个BusMSG接口,并且使得发送消息方法接受实现BusMSG接口的消息,真正的支持消息的自由实现。
(增加一个接口,而不是扩展原来的BaseMSG接口的原因是功能分离,用户模块仅需要看见对它们有意义的接口,不用关心总线内部发生的事情)

* 实现了前面提到的,较为通用"跨模块调用"(Inter-module call, IMC)消息支持。
本次修订之前,对于IMC的支持是硬写在消息队列核心的,因此不具有自定义实现以及扩展性。
现在添加一个MSG_Event接口,为消息在总线的传输添加通用的事件,然后将IMC消息支持实现在这些事件的基础上。
这样,不仅自定义性得到满足(可以重新实现自己的IMC消息),而且也具有比较好的扩展性(利用事件支持,还可以实现一切其它的功能)。

* 将扩展消息模单独块化。(上图中不可见)
本次修订之前,扩展消息和基本消息挤在一个模块里面。现在基本消息模块仅含有必须的消息接口,减少不需要扩展消息的模块的代码量。

* 优化扩展消息的实现。(上图中不可见)
可复制消息和可序列化消息的代码经过重新梳理和测试,效率和可靠性都得到提高。

--- 线程和线程池 ---

* 重写了线程回收代码
本次修订之前,对于需要强制回收的线程使用的是TerminateThread,这种方法不能有效回收系统资源,而且也不能给目标线程回收用户资源的机会。
现在则采用了改变线程上下文的方法,并且分两步回收线程: 首先强制生成一个例外,如果一定时间内还不能中止,再强制让线程自我中止。
若第一种方法成功,所有的资源应该得到完美的回收 (当然,前提是执行的程序有良好的意外处理机制);
第二种方法能够回收系统分配的线程资源以及部分已知的用户资源 (尽管不能完全回收,但是比TerminateThread什么都回收不了的好 =v=)。
另外,顺带重新包装了OS的一写同步对象,以保证必要时能够及时唤醒等待中的线程。

* 为工作线程增加接口
修订的过程中,我突然想起,分配线程给用户模块的时候用的居然是类.... |||||| 现在改正为接口了,同时对于线程对象的中止条件进行了完整的测试,保证不论线程以何种方式结束(自杀或者他杀 :P),都不会导致线程对象和线程池出问题。

Delphi包装的线程拥有良好的自定义性能。因为支持纯虚函数指针,任何类实现线程方法都可以通过工作线程成为一个独立的执行体,没有任何对象继承的要求。

--- 意外对象 ---

Delphi的另一个特色就是对意外处理有优良的包装,同过抛出意外处理异常情况能够提供比返回值有用很多倍的信息,而且随时随地,想抛就抛。

本次修订新增了一个独立的意外对象(而不是继承自默认的意外对象),特点就是,能够自动叠加(Stack)其它的意外对象,形成完整的"意外链",对于代码的除错非常的有用,特别是在没有源代码或者调试器的情况下。
简单的讲,就是在这个意外对象被抛出的时候,如果当前已经有一个已经抛出的对象,则新的意外对象将旧的自动"链接"到自己内部。

这个功能在许多语言里面几乎是无法想象的。比如Java中新的意外将会覆盖掉旧的意外;C++则更狠,如果抛出新的意外时旧还没释放,程序强制调用ExitProcess,世界太平...|||| (这就是为什么C++强烈不建议在Destructor中抛出异常)

新CAS的意外对象不仅可以提供文字和数字反馈,还可以提供出现意外的类(不仅仅是名字,而是一个类的引用),方法的名称,以及更加有用的信息: 一个意外分类代码。
这个分类代码表示了出现这个意外大致的原因,已经可能的问题。例如:
User类意外表示这个错误是由于程序员不小心引起的,比如提供了一个超过数组范围的编号;
Functional类意外表示这个意外是功能的一部分,比如,如果用户选择通过意外而不是返回值通知消息发送超时,将可能收到Functional类的意外;
Panic类意外表示可能出现了一些不寻常的问题,比如系统内存耗尽;
Internal类意外表示可能总线的代码有问题,这个时候就需要通知我来解决啦! :D

------

最后,来一张消息总线的全景。



(注意,程序未正式发布前,你所看到的东西都有可能改变.....尽管几率比较小......)
dwing - 2007/6/2 20:36:00
不做评价...直到开放SDK. [em2]
Prz - 2007/6/3 0:00:00
原帖由 dwing 于 2007-6-2 20:36:00 发表
不做评价...直到开放SDK. [em2]




唔,对了,目前来说,CAS对于其它语言的兼容性并没有作太多妥协。

1. 对于一些Pascal原生的对象,CAS模块是将其作为基本元素直接传递的。比如,String对象,集合(set),记录类型(record)等。

2. Delphi提供的标准核心构件,也是被当作基本元素直接传递的。比如,文件(File),流(Stream)等。
(以上两点呼应我曾经提到过的,CAS仅仅是按照COM规范使用Interface实现模块代码分离,而不是完全的为COM而COM)

3. 比较重要的是,CAS模块与总线、模块和模块之间的过程调用没有Exception Firewall。
这样做的好处是能够为模块编写者提供全面的Debug信息——前提是,如果模块能安全的够接受异常对象并展示出其内部含有的信息;
但是,就像我上面提到的,不是所有的语言都对异常有完善的支持,尤其是对于C++来说,任何时候都可能抛出异常几乎是不能接受的.....


至于提供跨语言兼容性嘛,我还不太确定什么时候会这么做。或许直到Haeleth (RLDev的作者)把RLDev 用C++ 重写的那一天吧.... ||||||||
dwing - 2007/6/3 9:54:00
原帖由 Prz 于 2007-6-3 0:00:00 发表
唔,对了,目前来说,CAS对于其它语言的兼容性并没有作太多妥协。

1. 对于一些Pascal原生的对象,CAS模块是将其作为基本元素直接传递的。比如,String对象,集合(set),记录类型(record)等。

2. Delphi提供的标准核心构件,也是被当作基本元素直接传递的。比如,文件(File),流(Stream)等。
(以上两点呼应我曾经提到过的,CAS仅仅是按照COM规范使用Interface实现模块代码分离,而不是完全的为COM而COM)

3. 比较重要的是,CAS模块与总线、模块和模块之间的过程调用没有Exception Firewall。
这样做的好处是能够为模块编写者提供全面的Debug信息——前提是,如果模块能安全的够接受异常对象并展示出其内部含有的信息;
但是,就像我上面提到的,不是所有的语言都对异常有完善的支持,尤其是对于C++来说,任何时候都可能抛出异常几乎是不能接受的.....


既然这样,我就不期望什么了,果然是太依赖delphi本身提供的各种"便利".
我还是去研读跨语言的COM好了.

--------------------

另外,关于C++的异常.以下代码在很古老的VC6中就运行无误了.
包括构造函数异常,析构函数异常,嵌套异常,异常中的异常.

#include <stdio.h>
class C
{
public:
C(bool b) {if(b) throw new int(1);}
~C()  {throw new int(2);}
};
void func()
{
try{C c0(true);}
catch(int *e)
{
  printf("%d\n",*e); 
  C c1(false);
}
}
void main()
{
try{func();}
catch(int *e)

  printf("%d\n",*e); 
}
}

输出:
1
2
Prz - 2007/6/3 15:55:00
赫赫,楼上您并没有"嵌套"啊。是不明白还是又在偷换概念?(估计是后者.... -_-#)

试试下面这段:
----

#include <stdio.h>
class Cx
{
public:
Cx() {throw new int(1);}
~Cx()  {throw new int(2);}
};

void main()
{
try{C c0;}
catch(int *e)
{
  printf("还没有到这里世界就太平了...||||");
}
}

---
当然,我没有玩过M$的C++不知道M$是不是又擅自作了非标准的"修订"。
至少标准的C++是一定会"太平"的....||||||
dwing - 2007/6/3 16:29:00
原帖由 Prz 于 2007-6-3 15:55:00 发表

试试下面这段:
----

#include <stdio.h>
class Cx
{
public:
Cx() {throw new int(1);}
~Cx()  {throw new int(2);}
};

void main()
{
try{C c0;}
catch(int *e)
{
  printf("还没有到这里世界就太平了...||||");
}
}


VC6的运行结果:
还没有到这里世界就太平了...||||Press any key to continue

这个异常符合C++标准,为什么说M$擅自作了非标准的"修订"?
Prz - 2007/6/3 16:50:00
哦,可能我简化的过分了点。
(Delphi在类Construct的时候如果有异常,会自动调用Destructor。可能 Static 方式定义的C++不会)

下面这个应该表明我想说的问题:
------

#include <stdio.h>
class Cy
{
public:
Cy() {}
testexcept {throw new int(1);}
~Cy()  {throw new int(2);}
};

void func()
{
Cy c1;
c1.testexcept();
}

void main()
{
try{ func(); }
catch(int *e)
{
  printf("真·还没有到这里世界就太平了...||||");
}
}

------

原因很简单,当一个异常抛出的时候,控制的跳转可能导致一些对象Out of scope。
为了程序逻辑的完整性(以及不产生内存漏洞),对象的Destructor会被自动调用。
但是,如果这个时候Destructor再一次抛出异常的话,怎么办呢?
不管是忽略掉第一个异常还是第二个异常,都不是一个可以接受的解决方案。

C++在这种情况下规定程序强制退出;
Java将其前一个异常扔掉;
Delphi默认动作和Java一样,但是留给编程者自己处理的空间,我选择将异常“串”起来,继续传送。
dwing - 2007/6/4 8:59:00
其实析构函数经常只做一些内存和资源释放,这些操作都是安全的,一般情况下都不会也不需要抛出异常.
如果确实要抛出异常,那要这么做:

#include <stdio.h>
class Cy
{
public:
        void testexcept() {throw new int(1);}
        ~Cy()
        {
                try { throw new int(2); }
                catch(int *e) { printf("%d",*e); }       
        }
};

void func()
{
        Cy c1;
        c1.testexcept();
}

void main()
{
        try{ func(); }
        catch(int *e) { printf("%d",*e); }
}

运行结果: 21

---------------------

参考: 对象生死劫 - 构造函数和析构函数的异常
http://blog.csdn.net/tingsking18/archive/2007/03/05/1521296.aspx
Prz - 2007/6/4 15:30:00
这些方法我当然知道,不然就不会提出来了。
虽然经过层层的Try Catch包裹可以(基本上)解决问题,但是用起来不爽嘛,感觉像“套中人”......
还有,在图形界面下,没有console给你print,那么就要写自己的Debug Window...... 多线程还需要同步机制,弄得不小心还会死锁一下......

最符合正常人的思维的写法应该是,有问题就扔╰( ̄▽ ̄)╭  ........
到最后一张网接住,看看网到了什么东西。

我个人认为这个才是最体现异常处理的精髓的。
Prz - 2007/6/4 16:12:00
当然,我同意在Destructor中扔出异常不是什么好事,我的程序里面也很少用。但是,C++的问题在于:
"不要在Destructor中扔出异常",这点表面上看起来很好做到,但是实际应用中......

虽然保证不出现明确的throw很简单,但是你不能保证一个Destructor仅仅调用其它的Destructor;
如果Destructor调用了一些会抛出异常的方法,那其实是和在Destructor中抛出异常等效的。

这样一来,在Destructor中要使用的方法也就不能轻易的抛出异常了;
在Destructor中要使用的方法要使用的方法也不能轻易的抛出异常了;
在Destructor中要使用的方法要使用的方法.....要使用的方法还是不能轻易的抛出异常了...
.......犹如多米诺骨牌一般哗啦啦.....的倒下去......|||||||||


因此,在一个稍微中型的C++工程里面(特别是多人合作的),不要使用异常几乎成了工业标准!(大型的就更不用说了)
因为一旦某人使用了异常,并且别人对于抛出的异常没有准备,整个程序就会死得很难看,而且要找出问题极其的麻烦....(特别是双重异常造成的直接ExitProcess)

如此一来,"异常处理"这个非常有用的调试机制反倒成了调试软件的绊脚石,岂不是很荒谬么?
dwing - 2007/6/4 19:48:00
异常这东西我在写实际的程序中基本不用.因为既不使用抛出异常的库,自己也不需要抛出异常.
可能比较有用的异常是操作系统发出的,那个异常不是跨平台的,因此VC自己定义了一套__try/__except关键字来处理,这样就可以防止程序写无效指针等操作导致的立即退出.我不知道delphi如何做到这点的.
非console的大型工程一般都是写log文件的.log类只要做一下临界区处理就可以了.
Prz - 2007/6/4 20:59:00
Delphi对异常处理有着完全不同的理念——既可以当成调试功能,又可以当成开发功能,甚至成为设计的一部分。(因此支持得很到位)
Delphi中所有操作系统的异常都被转换为Delphi的异常,因此对于Delphi编程者来说,异常是跨平台的(因为Linux的Signal也被包裹成统一界面的异常对象)。
Prz - 2007/6/9 13:58:00
很久没有写K.O.这边的东西了。尽管以前的进度都快忘了,刚才打开工程组,稍稍浏览了一下各个模块的消息定义,立刻就可以开始往下面继续工作了。
看来CAS的重要目的之一似乎达到了期望的效果:低损耗暂停、恢复工程进度。

K.O.距离Alpha版本还有一小段时间,不过既然前面已经铺垫好了CAS架构,那么现在就可以方便的介绍一下K.O.汉化工具的大体雏形了。
K.O.使用运行级别为1的CAS"基本消息总线",程序结构大体上符合Prelude to K.O. (4)中介绍的"应用程序"型。

---
主程序结构(构建中):

因为整个程序(包括主程序)都采用模块化设计,因此内含两个虚拟模块:

UIService 位于 "KOLoaderMain" 中,负责处理图形界面的输入和反馈;
Controller 位于 "KOLocalizerController" 中,负责将用户的请求分解成各种操作,并分发到各个模块,最后收集结果并反馈给 UIService 模块。
同时 "KOLocalizerController" 中包含了程序所有模块的加载/卸载操作过程(由主线程于开始/结束时执行)。

Controller与各个模块的交互仅仅通过消息,因此需要引用各个 "_______MSG" 用于生成相应模块能够"明白"的服务请求。
(因此,目前这个工程的大体进度也暗示在这张图中了。)

---
消息总线模块: 内部结构图已经在贴顶介绍过了。

---
脚本包支持模块(完成):



用于将流形式存在的脚本包分解成为各个独立的(未解码)脚本流、以及反向操作。

---
脚本编码/解码模块(50%):



用于编码/解码独立的脚本流。(解码部分完成,编码部分还没有从旧项目中移植过来...-v-)

---
数据库支持(刚开始):



用于统一存放/管理脚本、原文以及翻译文字。

---
翻译导入/导出模块: 用于从数据库中提取翻译文字,储存为文本文件;以及反向操作。

---
Reallive指令识别: 用于准确,完整的从解码后的脚本中提取原文。

---
Reallive文本导入模块: 用于快速将翻译文字导入解码后的脚本中。

---
文字转码模块: 将翻译文字"安全"的翻译为Reallive引擎可以接受/外挂DLL可以解码的文字。

---
配置文件支持: 用于将必要文本导入信息写入外挂DLL的配置文件中。
KEY的心情 - 2007/6/10 6:52:00
某毕业生只会C++和J2ME的飘过,最有成就的就是在毕业设计2个月中自己写了个3D的简单游戏。
决定以后往3D图形处理方面发展了。
keakon - 2007/6/10 7:49:00
很明显的不良编程习惯

《effective c++》里有条铁则,绝对不要在析构函数里抛出异常=。=

程序的正常运行很多情况都是建立在,或者说逻辑上都需要析构函数不抛出异常
==================
PS:异常不是用来调试的,是将运行时错误处理与逻辑过程相分离

不知道delphi干嘛要把异常弄得那么复杂,异常处理又不是不要代价的

何况已经抛出异常了,在程序中捕获,打印异常链/栈就知道哪错了=。=
Prz - 2007/6/10 12:06:00
楼上估计不明白我前面说的"雪崩效应"。
C++中如果还没等捕获到异常的时候程序就和谐了,怎么打印?

另外,Delphi的异常复杂么?
keakon - 2007/6/11 7:59:00
析构函数不抛出异常就行了啊=。=

在里面catch是它的义务,不要把责任推给调用函数,那样增大了耦合度,也不利于封装性

从设计角度,没什么原因需要你析构时抛出异常;能想到的就是提示析构出错(比如关闭文件遇到IO错误)和纯粹为了恶搞

这种情况在析构函数内调用一个函数对象(事先自己注册和定义提示错误的方式),调用后返回就行了;更改那个函数对象,也可以做到更改显示方式;要避免重新编译,将其定义成接口,然后用不同方式实现该接口即可
================
复杂是说异常没必要帮助调试,感觉太过依赖这个不是什么好习惯=。=
Prz - 2007/6/11 12:58:00
析构函数不抛出异常就行了啊=。=

不仅如此,还必须在里面层层套用try...catch,头晕。

在里面catch是它的义务,不要把责任推给调用函数

出现异常,如果不是能够立刻被处理的,那就应该是交给调用者。
因为调用者处于整个软件命令链(Chain of Command)的高端,只有它才拥有足够的信息来作出正确的判断。
仅仅打印出来不叫处理,而且这才更像是在“帮助调试”,用您下面的话形容,“不是什么好习惯”。

那样增大了耦合度,也不利于封装性

整个工程采用统一异常对象,不仅利于封装,而且利于简化处理过程,减少代码出错的可能。

从设计角度,没什么原因需要你析构时抛出异常

从设计的角度,你很难保证析构时调用的所有函数都不抛出异常,不论什么原因。
当然,除非根本不用,这也是目前大中型C++工程的标准。

复杂是说异常没必要帮助调试,感觉太过依赖这个不是什么好习惯

似乎C++在析构时截获异常只能打印处理,然后丢弃,更像是在“帮助调试”。
CAS中将所有抛出的异常完整的保留并传向调用者,由调用者决策处理,这样才可能实现将异常融合于设计中。


------

哦,对了,似乎楼上最后的观点逆主流而行啊:(有可能是一激动说反了 =v=)
我的认识是:
异常作为调试功能是非常成熟而且有效的;但是因为异常的处理相对于返回值有较高的资源消耗,因此如果频繁的用于设计中,会降低程序运行的效率,“不是什么好习惯”。


这一点CAS已经考虑到了,因此绝大部分的异常都是为调试服务的,正常运行中的程序流程很少会抛出异常。
如果有将异常作为设计功能的函数,CAS一定同时提供了不抛出异常的版本,供程序设计者自由选择。
dwing - 2007/6/11 18:12:00
不仅如此,还必须在里面层层套用try...catch,头晕。

C++是非常灵活的语言,适当利用智能指针可保证内存自动回收,这样就不用层层嵌套try...catch了.
如果使用带有引用计数的share_ptr或句柄模板类,那基本不需要手动delete任何对象了.
话说回来,delphi不也会面临层层套用try...catch的问题吗?

似乎C++在析构时截获异常只能打印处理,然后丢弃,更像是在“帮助调试”。

C++异常类可以设计成异常链的形式继续抛给上层.
不过构造异常类对象也有可以抛出异常,这样上层得到的异常对象要么不存在,要么描述异常的信息不完整,delphi也应该会遇到这种问题.

因此绝大部分的异常都是为调试服务的,正常运行中的程序流程很少会抛出异常。

异常主要不是用来调试的,调试不能因异常而停止后面代码的测试.
高效的调试方法应该是设置断点,assert,单步执行和查看变量.
异常是一种更方便快捷地处理无法继续运行的问题的机制.

从设计的角度,你很难保证析构时调用的所有函数都不抛出异常,不论什么原因。

析构确实没什么理由需要抛出异常,所有释放资源的函数都不需要抛出异常.
除了资源回收,析构还需要做什么呢?
我的原则是构造和析构都仅仅对成员变量做最基本的初始化和释放,不把复杂的工作放到构造和析构中.
如果真对析构中调用的函数不放心,就用cache(...)忽略掉或做个log好了.如果资源释放都不能如愿,对象析构也就没什么意义了.
Prz - 2007/6/12 0:29:00
C++是非常灵活的语言,适当利用智能指针可保证内存自动回收,这样就不用层层嵌套try...catch了.

据我有限的理解,所谓的智能指针仅仅是保证在对象不再被引用的时候自动“析构”而已,和异常处理风马牛不相及,不用try...catch一样会挂。
如有错误,请纠正。

话说回来,delphi不也会面临层层套用try...catch的问题吗?

估计你还是对Delphi的异常处理的能力没有概念,不过可以理解,就算一般的初等Delphi玩家也会有相同的错误概念。
不过看在您也算C++的经验玩家的份上,我就不点得太明白了:
*在双重throw的时候不会挂掉,就算不catch;任何使时候throw出去的exception都可以被接管,就算不catch* <--- 从这点开始发散您的思维,想象一下能够实现什么吧。

不过构造异常类对象也有可以抛出异常,这样上层得到的异常对象要么不存在,要么描述异常的信息不完整,delphi也应该会遇到这种问题

好像您有点晕了...异常处理不是从构造异常对象时开始的,而是从抛出那一刻开始的。
构造一个异常的时候,这个异常并没有抛出,对于机器而言,这个构造完全可能是正常的程序过程。这个时候如果发生异常,抛出的当然将是另一个的异常。
而且Delphi中永远不会出现构造不完整的对象 (解释就在您前面引用给我看得文章中),因此这种情况也不会出现C++中可能出现的内存漏洞问题。

异常主要不是用来调试的,调试不能因异常而停止后面代码的测试.
...异常是一种更方便快捷地处理无法继续运行的问题的机制.


这两句话明显自相矛盾:
如果程序遇到异常而可以不停止调试,那就证明程序可以继续运行下去,那么也就没有必要使用异常
(反映到实际使用中,比如分配、调用资源的失败,都可以用返回值,没有必要使用异常);
如果程序真的无法继续下去,那么抛出异常将是唯一的解决办法,而且是最好的调试方法,因为无法通过返回值处理
(反映到实际使用中,比如引用失效的指针、内存位置,没有有意义的返回值,有必要使用异常)。

我同意的说法是:
异常不是只能用来调试的,调试可以不因异常而停止后面代码的测试.
...异常是处理无法继续运行的问题的不可替代的机制.


析构确实没什么理由需要抛出异常,所有释放资源的函数都不需要抛出异常.
除了资源回收,析构还需要做什么呢?
我的原则是构造和析构都仅仅对成员变量做最基本的初始化和释放,不把复杂的工作放到构造和析构中.
如果真对析构中调用的函数不放心,就用cache(...)忽略掉或做个log好了.如果资源释放都不能如愿,对象析构也就没什么意义了.


您因为自己的习惯而引发的概论只能证明您的群体合作经验比较少。
我相信您的描述可以自圆其说,但是,你不能将自己的意愿强迫到别人的头上。别人怎么提供别人的实现,这是与您怎么实现您的对象是非常不同的事情。
Delphi和C++异常处理对于一个人的几万行程序来说,基本没有区别。但是对于多人,特别是以松散方式合作的组来说,C++异常的scalability要差得多(这里的意思是,随着规模的扩大而导致可用性的快速降低),这个是不争的事实。

当然如果你能强制别人按照你的思路来办事情,这样会好得多;
其实,如果强制所有人按一个人的思路来做事,世界上很多问题也都可以很好的解决。
但是问题是,无数的历史事实证明,独裁的结果肯定是快速灭亡,因此不是一个解决问题的办法。
dwing - 2007/6/12 9:26:00
汗,您讲的这些我并没有认同我说的有误,您对C++的理解和我对OP的理解半斤八两.我只好耐心地解释:

据我有限的理解,所谓的智能指针仅仅是保证在对象不再被引用的时候自动“析构”而已,和异常处理风马牛不相及,不用try...catch一样会挂。
如有错误,请纠正。

只要保证析构不抛出异常即可避免嵌套try...catch,析构不抛出异常这本来就是一条规定,就像判断指针是否为空之前不要随便赋值一样.C++的灵活使它能做到许多其他语言不能做到的很难想象的复杂功能,但也会出现滥用的后果,不能说核能危险我们就不去开发它.
VC的__try/__except机制才可以做到从出现异常的地方再次继续运行.不过我承认这个机制很危险,一般是没人用的.所以只有认为出现不可继续运行的严重问题时才抛出异常.

话说回来,delphi不也会面临层层套用try...catch的问题吗?

您可能没理解这句话的意思.
单层try...catch机制是不顾后果的,一旦抛出异常,异常后面的资源释放都无法继续执行,delphi怎么会避免呢?

这个时候如果发生异常,抛出的当然将是另一个的异常。

这就是我所说的一种情况,这里的"另一个异常"就没有前一个异常的信息了,用异常来调试也就"信息不完整"了.delphi构造任何异常都是要分配内存的,如果分配内存出现异常,则构造任何异常都会出错,最后即使能catch到,也只能得到分配内存错误的异常,而没有最初抛出的异常了.

如果程序遇到异常而可以不停止调试,那就证明程序可以继续运行下去,那么也就没有必要使用异常

这么说,异常就不是用来调试的.

(反映到实际使用中,比如分配、调用资源的失败,都可以用返回值,没有必要使用异常);

呵呵,在实际使用中,异常经常用来解决"分配、调用资源的失败"而无法继续运行后面的代码,这些才是典型的"异常"并跳出,不必每次都判断返回值并做频繁的错误处理.如果出现一般错误,才应该用返回值.

我相信您的描述可以自圆其说,但是,你不能将自己的意愿强迫到别人的头上。别人怎么提供别人的实现,这是与您怎么实现您的对象是非常不同的事情。

合作写程序如果不做出一些规范,那才是灾难.

Delphi和C++异常处理对于一个人的几万行程序来说,基本没有区别。但是对于多人,特别是以松散方式合作的组来说,C++异常的scalability要差得多(这里的意思是,随着规模的扩大而导致可用性的快速降低),这个是不争的事实。

天啊,您在哪里听说的无稽之谈,不妨给个参考让我好好拜读一下.没准我还真去"投靠"delphi了呢:)

但是问题是,无数的历史事实证明,独裁的结果肯定是快速灭亡,因此不是一个解决问题的办法。

C++是有标准并广泛存在的,delphi才有些独裁...这句话赠送给您更合适:)
Prz - 2007/6/12 11:13:00
只要保证析构不抛出异常即可避免嵌套try...catch,析构不抛出异常这本来就是一条规定,就像判断指针是否为空之前不要随便赋值一样.C++的灵活使它能做到许多其他语言不能做到的很难想象的复杂功能,但也会出现滥用的后果,不能说核能危险我们就不去开发它.
VC的__try/__except机制才可以做到从出现异常的地方再次继续运行.不过我承认这个机制很危险,一般是没人用的.所以只有认为出现不可继续运行的严重问题时才抛出异常.


模棱两可。请正面回答我的问题:您前篇里面企图把智能指针和异常处理混淆在一起,我认为是不相干的,我到底是错了还是对了?


单层try...catch机制是不顾后果的,一旦抛出异常,异常后面的资源释放都无法继续执行,delphi怎么会避免呢?


就用你自己的切身说法,析构仅仅是释放资源。那么异常发生,自然是比释放资源更严重的事情发生了,继续释放资源还有意义吗?
这个时候更妥善的解决办法是立刻中断下来,查找错误原因,而不是不负责任的写一句话到一个什么地方,继续作可能根本就是错误的事情,然后等一切都乱套以后,再对着屏幕发愣吧。


delphi构造任何异常都是要分配内存的,如果分配内存出现异常....


赫赫,请你不要天真地将自己不明白的东西愚蠢化。
象Out-of-memory这样的异常,如果不是提前预生成的,那就搞这个系统设计的人文凭一定是在什么菜市场买的.....

而且:
这就是我所说的一种情况,这里的"另一个异常"就没有前一个异常的信息了,用异常来调试也就"信息不完整"了


你用一个C++和Delphi以至于任何一种程序都会出现的问题,明显没有说明任何的作用。
举个简单的例子:
论题: 2007年生产的Core Duo的系统比1990年产的286性能优秀
你说: 将两台机器放在120摄氏度的烤箱,2分钟内两台机器都崩溃了,所以Core Duo系统并不比286好到哪里去
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||


这么说,异常就不是用来调试的


只要你不独裁,我尊重你的看法


呵呵,在实际使用中,异常经常用来解决"分配、调用资源的失败"而无法继续运行后面的代码


概念错误,"分配、调用资源的失败"绝对不是无法继续运行的错误。


合作写程序如果不做出一些规范,那才是灾难


因此对于异常处理规范性不佳的语言就不敢乱用异常。
就像知道自己不太能够控制用钱的人也就不会随便申请信用卡。
世界是理性的,广大人民是智慧的。


天啊,您在哪里听说的无稽之谈


知识爆炸的今天没有百科全书式的人物,可以理解。
使用Delphi的大中型项目中随处都找得到异常的使用;请找出使用C++的大中型工程中使用异常的例子?


C++是有标准并广泛存在的,delphi才有些独裁...这句话赠送给您更合适


我并没有叫嚣要强制规定别人怎么去编程,也没有叫嚣自己不喜欢的语言就要灭亡,因此相对您,我更不像独裁者。
哦,而且我也没有说是您是独裁者,那句话仅仅是对历史的反思,并不是送给谁的。
就像很久以前我批评"盗字"一样,和您一点关系都没有。
请您改掉喜欢对号入座的习惯,特别是看到负面的评价更是,不然这样不利于身心健康发展的。
dwing - 2007/6/12 11:53:00
模棱两可。请正面回答我的问题:您前篇里面企图把智能指针和异常处理混淆在一起,我认为是不相干的,我到底是错了还是对了?

智能指针的一个好处是能很好地处理异常时资源无法完全释放的问题.
在保证析构不向外抛出异常的情况下,借用智能指针的自动析构来释放资源是很安全便捷的.
而且也可在构造函数中抛出异常时自动释放资源.
而显式地释放资源就容易被try...catch机制忽略掉,而析构是不会忽略的.

就用你自己的切身说法,析构仅仅是释放资源。那么异常发生,自然是比释放资源更严重的事情发生了,继续释放资源还有意义吗?

我可不这么认为,如果真比释放资源还重要,那就像C++那样无法处理异常就退出好了.
例如我要读取文件中的内容并显示出来,这里就有可能出现许多无法继续执行的情况,如文件不存在,文件格式不正确,文件无法读取等等,
每次都写一堆if...else并且每次都要写一堆释放资源的代码是很烦琐的,这种情况就适合抛出异常,并在catch中统一释放资源.
这都不是致命错误,释放资源也是有意义的.

你用一个C++和Delphi以至于任何一种程序都会出现的问题,明显没有说明任何的作用。

我可没说C++在这个地方有优势,而是您说delphi有优势,而我反驳了而已,并没有C++在这个地方更好的意思.

只要你不独裁,我尊重你的看法

我可不是说要独裁,delphi的RAD是VC不及的地方,但要说写系统或底层程序,C/C++更适合.
(C/C++是能让编程者懂得每个步骤编译器是如何处理的对编程者要求比较高的语言.)
这不是我一个人的观点,而是事实上确实如此,我所见过的所有的操作系统,编解码器都使用ASM/C/C++,这是我"独裁"的结果吗?

概念错误,"分配、调用资源的失败"绝对不是无法继续运行的错误。

如果内存池无法分配,资源句柄无法分配,我想后面会有一大片代码无法继续运行了.

使用Delphi的大中型项目中随处都找得到异常的使用;请找出使用C++的大中型工程中使用异常的例子?

我见过许多写的很优秀的C++库都使用了异常,包括STL,BOOST,MFC等等.
在C++大中型工程中没遇到异常也是有许多原因的:
1.底层代码中很少遇到需要抛出异常的情况,像java那样高层语言,异常属于家常便饭了.
2.C++比较复杂,异常属于高级特性,非精通者不敢使用.
3.C/C++的工程许多都要考虑跨平台,而不可能保证异常一定被系统支持的.
4.使用异常容易使开发人员产生依赖感,对自己的代码不负责任.

因此相对您,我更不像独裁者

那么希望到开放接口时,不要像前面说的那样"CAS对于其它语言的兼容性并没有作太多妥协。"
(看看微软,虽然比较"独裁",但它的COM接口是非常开放的,从汇编到js脚本都能调用其接口.)
Prz - 2007/6/13 1:06:00
在保证析构不向外抛出异常的情况下,借用智能指针的自动析构来释放资源是很安全便捷的.
而且也可在构造函数中抛出异常时自动释放资源.


很遗憾的发现,你根本没有仔细阅读你自己在前面贴出来的那边CSDN的Blog。
构造异常的时候自动释放对象是Delphi的基本功能,不需要外加"智能"。


举个简单的例子:
论题: 2007年生产的Core Duo的系统比1990年产的286性能优秀
你说: 将两台机器放在120摄氏度的烤箱,2分钟内两台机器都崩溃了,所以Core Duo系统并不比286好到哪里去
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

我可没说C++在这个地方有优势,而是您说delphi有优势,而我反驳了而已,并没有C++在这个地方更好的意思.


你当然没有说C++有优势了,而且还没明白我的意思,我说的就是你的反驳方法无效。


我可不是说要独裁,delphi的RAD是VC不及的地方,但要说写系统或底层程序,C/C++更适合


这是您第一次提“系统或底层程序”。


如果内存池无法分配,资源句柄无法分配,我想后面会有一大片代码无法继续运行了.


请你仔细想想,程序的逻辑并没有处于“没有解决方案”的地步;
真正“没有解决方案”的,是直接访问非法地址,无效指针这些情况;
这就是为什么Windows只有在这些情况才抛出异常,其他的统统都可以选用返回值。
这个道理换成*nix也是相同的,只有可以数得出来的几个Signal,其他的都是返回值。


那么希望到开放接口时,不要像前面说的那样"CAS对于其它语言的兼容性并没有作太多妥协。"


请不要偷换概念:“不妥协”绝对不是“独裁”。

退一万步讲,我并没有强迫别人使用啊。

况且,不“妥协”并不是“无法使用”,聪明的程序员总是会找到一些精练高效的解决方案。
提示:Borland / CodeGear并不是没有C++的产品。

当然,对于M$我只有不好意思地说"Suck It"了。
M$不是一贯的以自己为标准,然后希望别人"妥协"吗......





---
另外,“独裁者”是怎么出现的?

纵观历史,“独裁者”都是因为其他人一步步地“妥协”,“独裁者”才得寸进尺、得尺进丈,成为真正的独裁者的。
所谓独裁,也就是将其他人的“妥协”认为是理所当然的。

因此每个实体,不管是个人、公司还是国家,都需要坚持好自己的原则,关键时刻决不妥协,才能避免重蹈前人的血泪悲剧。
dwing - 2007/6/13 9:13:00
很遗憾的发现,你根本没有仔细阅读你自己在前面贴出来的那边CSDN的Blog。
构造异常的时候自动释放对象是Delphi的基本功能,不需要外加"智能"。

自动释放对象只是针对成员变量在析构函数中释放吧.

你当然没有说C++有优势了,而且还没明白我的意思,我说的就是你的反驳方法无效。

不要因为你举了一个无法说明问题的例子就说我的反驳无效.

请不要偷换概念:“不妥协”绝对不是“独裁”。
当然,对于M$我只有不好意思地说"Suck It"了。
M$不是一贯的以自己为标准,然后希望别人"妥协"吗......

我可没说"不妥协"是"独裁",不要妄加推断.
没有正当理由排斥MS,是无法让人信服的.
不要总是提到"独裁者",你又不想针对我,那你想说明什么?
我不清楚你"不妥协"的是什么?
还有,不要总拿delphi和C++比较,二者不是同一层次的东西,C++不是MS独占的.
不要把话题转得太远,本楼辩论焦点是为什么delphi/object pascal的接口不去支持C++.是技术原因还是个人原因.
Prz - 2007/6/13 17:29:00
没有正当理由排斥MS,是无法让人信服的.


哈哈,我什么时候排斥了?您如果不是逻辑混乱,就是偷换概念的高手!
似乎从头到尾只有你一个人在明指暗点的排斥Delphi / Pascal。

说深奥了,大家不一定都看得明白,就用一个非常直接的比喻:
0. 我想要写一段相声,用四川方言;
1. 说普通话的dwing同学于是说:"你要想有前途的话,就必须要用普通话"。
2. 然后我反驳说,请不要歧视四川方言,语言是丰富多彩的,要学会欣赏各种不同语言的美。
3. dwing同学不买账。
4. 于是,我举出例子表明四川方言有很多用法可以表达出普通话无法表达的东西。

5. dwing同学反驳说,不论四川方言还是普通话,都有表达不出来的东西,因此两个都差不多。
(大概想表明,普通话对方言的优势就一定很优,劣势就一定可以忽略不计)
6. 我说,你这样说不对,很简单:世界上没有事物是完美的,就凭这一点我还可以把所有的东西都打成一片,那任何辩论都没有意义了。
7. dwing同学说,那还是不行,只要你说四川方言的优点就是排斥普通话!
8. 我说什么呢?无言... (不论用四川话还是普通话都无法形容....)

我不清楚你"不妥协"的是什么?


赫赫,都说了这么久了,您还不明白最开始您和我讨论的是什么?

不妥协,表示的是我会按照自己的意愿编写自己的程序。
不会为了某个人偏激的“前途论”而做出计划外的改变。


还有,不要总拿delphi和C++比较,二者不是同一层次的东西

只要不点破,这句话我们可以在保持自己的理解的基础上达成一致。
(点破了不好,光制造混乱,没有任何意义)


本楼辩论焦点是为什么delphi/object pascal的接口不去支持C++.是技术原因还是个人原因.


赫赫,请不要将自己的理解任意的加在别人的观点上。
就像前面我已经再三暗示过的,您的所谓“不支持”的理解是狭隘的。
您希望的支持,大概就是一定用M$扩展标准的VC写出原生代码,而且编程风格都要符合你的要求,你抓过来就用;
如果需要做一点点其他的工作,比如对象包裹,或者异常过滤,那就是"不支持"。

当然,这我不是做不到,如果你给我开工资单的话。

前面我已经提示了:对于一个精明的程序员来说,"不支持"的程序根本就是不存在的。
就拿CAS来说,使用对象包裹+异常过滤,M$C++应该就可以使用了;
如果觉得麻烦,还可以使用Borland / CodeGear C++,对于Delphi的兼容性要良好的多,很多工作ECO都做好了。

所以,从某种角度上来说,“不支持”的发生得确是“技术原因”,不过是使用方的。:P
dwing - 2007/6/13 18:03:00
Prz以上的言论都是为只支持delphi/object pascal找借口.
你说了这么多,明指暗点的排斥C++谁都能看得出来.
我不排斥你用什么语言开发,
但写开发包的一个目标尽可能对其他语言的支持(就像与外地人不要用方言交流一样).
如果仅考虑delphi,那我也没有办法.
这种做法永远不能像COM一样大度.
看看以后"方言"的CAS会有什么作为吧...

PS:不要总用"偷换概念","请不要将自己的理解任意的加在别人的观点上"这些话语来逃避自己失误.
cheong00 - 2007/6/14 0:56:00
亂入一下...

其實 Exception 的處理實在是很麻煩的. 即使用 try...catch... 結構也還是會有無法被處理的情形. (如 StackOverflow 做成的 stack corruption, 還有 OutOfMemory 的情況)

Constructor / Destructor 的 Exception 也不一定是可以避免的 尤其是在 MultiThread 的環境下, 當別人的 thread 要對你的thread進行Thread Abort 時, 也不見得會看你有沒有在new / delete object.

.NET framework v2.0 特別為這問題設立 System.Runtime.ConstrainedExexution namespace, 在 technet 有詳細介紹. 有興趣的可以一看.

原帖由 dwing 于 2007-6-13 18:03:00 发表
Prz以上的言论都是为只支持delphi/object pascal找借口.
你说了这么多,明指暗点的排斥C++谁都能看得出来.
我不排斥你用什么语言开发,
但写开发包的一个目标尽可能对其他语言的支持(就像与外地人不要用方言交流一样......

btw, 我確實沒有看到他排斥C++的地方.

寫開發包也不一定會對其他語言支持. 即使一些有名的如 TWAIN (控制 scanner 的 library )基本上也只支持 C++. Delphi 用的版本也只是高手們自已寫的 wrapper, 沒有官方支持. 而支援 .NET framework 的語言(不包括 C++) 只可以自己轉, 或者花錢買其他人做好的, 或者乾脆死心只支援有支援 WIA 的 scanner 了. 這在寫 SDK 的人來說十分正常. 要知道光測試一種語言可以使用的 test case 就有很多, 即使是要拿出去賣的也不一定會花時間支援其他語言的, 何況只是一個 pet project (指只是在工餘時間, 為自己的喜好自發完成的東西)?

最後一點就是, 要開發跨語言的 SDK 的話, 除非 compiler 本身已有很好的支援, 否則要為「非慣用」的語言做相容性調整可不是一般的困難及麻煩. 不想花額外時間在那些事情也是可以理解的吧?

這次 SDK 的發佈可見是有點公諸同好的性質, 我想要求在派禮物的同時還把禮物送到你家大門是否有點苛求呢?
dwing - 2007/6/14 9:02:00
楼上所说有理,但delphi的一些高级特性,用其他语言是无法做wrapper来支持的.
我并不强求支持其他语言,我只是提个意见,我见过太多的免费C/C++开发库,
对语言的支持度都非常高,微软的DX使用COM,语言兼容程度更不必说.
为什么对其他语言支持的开发库都使用C/C++,而delphi写的库就很少支持其他语言呢.
我认为C/C++很少利用编译器内在的东西,连字符串类型都需要自己定义类.
这样接口所关联的数据类型都是很基本的东西,其他语言都能很好地兼容.
而delphi等一些高级语言太依靠编译器本身提供的东西了(这些东西一般是没有源码参考的,
只有逆向去分析它们的结构),不能保证其他语言都会提供(可能连wrapper都无法写出),
否则就要在接口上做麻烦的转换,太习惯于以至于产生依赖感的人就不愿考虑这个问题.
而习惯于用C/C++的人由于很少利用这些东西,所以遇到使用黑箱操作的高级特性的接口,就会有排斥感.
Prz - 2007/6/14 10:14:00
Prz以上的言论都是为只支持delphi/object pascal找借口


因为你自己不想做自己应该做的事情而怪别人,我也没办法。

话又说回来,我看不出来“用Delphi写程序”为什么就一定有“兼容”C++的义务?
你的回帖里面搞得好像如果不嘴对嘴(就像大鸟喂小鸟)的支持C++就是一种罪过似的,不是“偷换概念”“变相攻击”是什么呢?


而delphi等一些高级语言太依靠编译器本身提供的东西了(这些东西一般是没有源码参考的,只有逆向去分析它们的结构),


信口开河的习惯又来了。
Delphi中所有的对象和语言元素都提供了原代码,就算是编译器内部的对象比如字符串类型,都提供了"伪代码"演示结构。

通过自己进行对象包装,完全可以实现Delphi到C++的兼容,而且也不是什么"黑箱"——因为包裹代码都是你自己写的。(反过来要我提供兼容性才是真正的黑箱)
如果你因要说"不兼容",那么一则可能是出于懒惰不想写代码,二则大约是出于自大,对其他语言不屑一顾,自然也就不明白怎么写。
所以说,您说的"不兼容",得确是属于"技术原因"+"个人原因"。
12
查看完整版本: [M] Prelude to K.O. (4.5) + 12楼(4.75)