- 论坛徽章:
- 2
|
回复 #6 gtkmm 的帖子
我说说如果我遇到这些问题了,打算如何处理吧。
仅仅是讨论啊…… 这种没个绝对判断标准的设计问题, 谁也别指望能说服谁 除非他自己愿意相信。
我也只是抛出自己的观点而已, 如果你觉得有点意思, 那握个手^_^ 如果觉得是胡说八道, 那你继续采取你的想法做也没关系。
我也希望能听到你的看法, 人总是狭隘的…… 如果你的看法把我说服了, 我还得感谢你~_~
差不多就这个讨论基调吧……
xlib我只知道它是干什么的,不了解它具体接口是怎样的…… 据说是设计得很底层,仅提供最小化接口。
在这上面直接编程可能会比较痛苦。 所以提出一个新的概念, 让程序员在这个概念下工作, 而不接触xlib, 是有意义的。
posix我了解得多一些。我的中心想法是:与其浅包装,不如不包装。
1. 如果浅包装, 完全不懂posix是不行的吧?
既得了解一个新的库, 还要了解它底层的posix。 这很痛苦。
所以,可以假设使用该库的人, 依然必须了解posix吧?
2. 那么, 浅包装的目的何在?
2.1 绝对不应该是编写大量代码, 让 function( object, param) 的语法, 变成 object.function( param );
这很没意思。 前者同样是通过object认可的方法去操作object,而不是直接拨弄object的数据。
这可能被称为数据隐藏, 不变式维护, 还有修改点限制什么的。 嗯,也是很多人口中的封装的意思。
而OO中的封装部分, 我觉得达到这点就足够了。 这就是OO中封装的核心。 object.function( param ); 只是语法样子不同而已。
而且,不一定所有c api,都可以以这种方式,转换为c++api 。
比如FILE* 和 fscanf。 c89我记得是没有vfscanf的。如果我没记错:
1. 所谓的File,要么不能实现原有的fscanf —— 功能少了
2. 要么必须把内部的FILE*暴露出来 —— 那还不如干脆不做这个事情 ……
这只是一个例子, 好像c89某个修订还是c99加入了vfscanf。
2.2 绝对不应该限制原有的功能
一旦限制了原有的功能, 当需要该功能的时候, 用户不得不抛弃一切美丽的封装, 直接访问底层。
2.3 既然完全不改变原有的编程模型, 浅包装的目的, 无非是想让某些时候编程方便一些。
这里得限定一下, 所谓的浅包装, 就是该层和下层之间通常仅仅是转发,几乎不带什么多余逻辑。
如何不包装, 也能让编程方便一点? 这个只能有一说一,有二说二了…… 我一下子也想不完……
一个一个说吧。
原帖由 gtkmm 于 2009-12-7 02:45 发表 ![]()
还有些函数,总回某一个值表示出错,但出错的机会很小,如果这种函数被调用几十次,到处写错误处理也很烦,最可怕的还是这些出错处理是类似的,结果到处写一遍。
嗯,这是C语言一个不太方便的地方 —— 没有异常。 没有异常会导致无法自动对某个错误保持中立。
异常中立我就不多解释了,既然你打算用异常解决这个问题, 我相信你也是因为C语言在这点上让你难受了。
我的想法嘛…… 在C++中真要解决"自动对错误保持中立", 那还是得用异常, 没别的办法……
但可不可以这样…… 虽然有点土气……
error read(int fd, param ); // posix
void my_read(int fd, param ) {
error e = read( fd, param );
if ( has_erroe( e ) ) throw some_exception( e ) ;
}
也就是说, 不要弄到一个File类里去。 让程序员依然是在posix下工作:
int fd = open( ... );
error e = read( fd , .... ); // 我想自己处理
my_read( fd , ... ); // 我不处理
注意, 如果某个程序员对你提供的my_read中的 has_error 不满(read返回值只有0和-1?) :
他不调用my_read就可以了…… 不用放弃你整个File类。
如果他觉得某种 his_has_error 是经常的情况, 他自己写一个 his_read 就ok了。
原帖由 gtkmm 于 2009-12-7 02:45 发表 ![]()
3. 如果函数在某种情况下要提前返回,但返回前,必须要做一些工作,比如解锁,释放内存,如果这种提前返回的地方很多,那就要写一大堆类似的函数了。
对于3,如果那些工作由一个类来处理,就会自动析构了。
解决3这个问题, 你需要的不是一个类, 而是RAII。
甚至都不是RAII, 而是RRID —— Resource Release Is Destruction 。 这东西有现成的,Loki::ScopeGuard, 就2头文件only。MIT许可证。
f()
{
int fd = open( ... );
if ( fd is invalid ) throw ... ;
LOKI_SCOPE_EXIT(close, fd ); // 分配后成功后,立即锁住资源
char* buf = malloc( ... ); // 或者是某个可以抛出异常的版本。
if (!buf) throw ...;
LOKI_SCOPE_EXIT( free, buf ); // 分配成功后, 立即锁住资源
下面的代码中, 可以自由的使用任何跳转, 除了longjmp。
}
Loki::ScopeGuard 也有自己的缺点, 比如它是rollbackable的。很多时候并不需要回滚功能。(回滚功能会稍稍做点无用的事情)
第2个缺点, 它不能作为成员。
ScopeGuard还是不错的…… 本来我以为类似它这样的语法,没有auto是会很难看了…… 结果被Alexandrescu一个const引用搞定了……
它提供了一种思路 : 范型的RRID或者rollback。
可以在这上面发挥一下, 做一种不带rollback功能的, 可以作为成员,嵌入到内中的, 没有额外开销的, 范型RRID 。
这个我有点想法, 不过太懒…… 没去深入想…… 等着C++0x流行后, 有decltype会比较好办一些^_^
如果你的目的就是提供一个类…… 那就继续提供吧……
原帖由 gtkmm 于 2009-12-7 02:45 发表 ![]()
4. 有些库为了所谓的跨平台,封装的层次太深,根本没有办法调试。
对于4,我觉得浅封装posix,先不管其它的库。 至于其它平台,那就是先用posix封装那个平台,之后就能用这个库了。
嗯, 层次太深是让人头很大的地方…… 尤其是C没有异常…… 要把底层的错误给带出来…… 那叫一个痛苦……
比如直接返回值代表错误含义的, 就不说了…… 一层一层往上返回吧……
也有返回值仅指出对错的, 用lasterrror查询具体原因的。 这样比较容易记忆, 但依赖于tss。
还有《C 接口与实现》中模拟出的TRY, CATCH,再深入一些就差不多可以做到自动错误中立了。
不过不太敢用…… 毕竟这东西,在C语言中, 算是非主流吧? 会雷焦别人的 ……
其实, 所谓的跨平台…… 说穿了, 就是引入一个中间层次……
需要跨平台的人, 使用这个中间层次。 实现跨平台的人, 再每个平台下实现中间层次。
你现在是打算建立一个新的中间层次, 是吧?
其他这种库中, 也建立了中间层次, 为什么它们最后变得层次很深? 总是有点原因的…… 设计太差?
可不可以换一种方式? 完全不要这个中间层次? 其实我的意思是, 使用现有的中间层次 …… 就是posix。
然后在不支持posix的操作系统上(嗯,好像就一个), 实现posix ……
完整的posix我不了解。 我最了解的是pthread部分。 好像就几个特性没把握,其他都可以实现。
比如barrier, 这应该要涉及汇编, 不懂……
比如cancel机制,我不太了解pthread中这个cancel机制…… 这是和win32 不同的一个地方, win32没有这种东西。
有cancel的编程经验后, 应该也不难。
这样的话,写的是pthread代码(比较多的人熟悉), 在win32下链接一个库(逃不掉的工作),在posix下,几乎不用你做什么多余的事情,链接pthread即可……
我知道有个pthread-win32的库…… 不过对他有些地方不太满意。
比如它几乎所有对象都动用了动态内存, 而内部又使用malloc …… 二进制兼容性可以通过其他方式解决, 不透明指针类型不是唯一的办法。
比如它实现STATIC_INITIALIZER的方式 …… 虽然我个人觉得pthread的STATIC_INITIALIZER就是一个错误…… 至少应该将它降级为optional。
我讨厌这种 get_and_init语意的接口。 用起来确实会爽一点点, 但不用也死不了。
要实现这种 get_and_init语意的接口, 会遭到很多很多麻烦。
心里隐约有一个依然是get_and_init;但是一旦init完成,以后的get_and_init调用就不再需要if测试,仅仅get的方案。
应该是可以实现的。 具体没仔细想。
稍微大点的库,都要用到动态内存分配的。 很多库就很不负责的直接使用malloc了 ……
所以我一开始就是问你对这个有没有什么想法…… loki.smallobj 在我的毕业论文里被批得半死, 哈哈哈
底层要么用malloc, 要么用new, 没别的定制方式; 那个独特的char index设计, 让chunk数量增多; vector<chunk> 本身就要消耗动态内存 ……
有一点可以改进的地方, 就是归还时, 找block 所属chunk, 可以将chunk组织为树,就不需要线性查找。
Alexandrescu大牛在MCD里将归还情况分析来分析去的, 居然没想到这个…… 不知道是不是考虑移植性问题了……
当然, i386下肯定没问题, 其他平台下 …… 指针的比较语意是怎样定义的我不清楚了…… |
|