cexer

cexer
posts - 12, comments - 334, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理
共4页: 1 2 3 4 
re: GUI框架:消息检查者 cexer 2009-11-22 20:07
@OwnWaterloo
【boost有自己的苦衷。它需要一个简短的东西来做占位符。】
你误会我的意思了。我是同意你的,我的意思是确实应该避免使用,因为像 boost,stl 之类的库很多地使用了这样的下划线。

【将_HandlerMapIter改为HandlerMapIter或者HandlerMapIter_真的没有坏处。
本来就是一个private,库用户不可见的名字。属于Handler,也不会和其他重名。还避免了(可能微乎其微)与_HandlerMapIter宏冲突。】
嗯,明白你的意思。其实这是我的习惯,外部不可见的都加个下划线在前头,没考虑到与标准库发生冲突的情况,你想的更细致一点。

re: GUI框架:消息检查者 cexer 2009-11-22 14:48
@OwnWaterloo
【刚醒…… 一边吃饭一边先把不涉及风格的东西解释一下……】
你们那里没有出太阳?这么大好时光不要拿来睡觉浪费了。回复完这个,我就出去晒太阳了。

【调用的是子类的函数,使用的是父类的数据(被切割了)……】
函数指针和虚函数的唯一区别就是函数指针少个读表的动作。所以如果说这样破坏了“不变式”,那么虚函数也是一样的。使用子类函数读父类数据,这种手法是经常被用到各种模式中的,这里也不存在切割问题,因为已经没有什么可以切割的。

【语言保留的标识符还分了几种, 统一起来就是以下划线开始的程序员全都不能使用。】
这只专家们一个很学究气的建议,跟“谨慎使用内联函数”一样的警示级别。因为标准库的作者确实使用了一些下划线的变量,boost 也有很多下划线开始的东西,像占位符 _1,_2,..._n。为了避免名字冲突确实少用为妙,但用了也不应该看作错误。

re: GUI框架:消息检查者 cexer 2009-11-22 13:06
@Loaden
谢谢老邓!也希望你的框架再进化!

re: GUI框架:消息检查者 cexer 2009-11-22 12:33
@OwnWaterloo
【强行使用class, 会使得需要定制的时候,就需要定义一个类 —— 其实仅仅需要定制一个函数就可以了。】
这样的设计也是跟框架的整体设计有关系,以后写了消息映射者你再看看。倒是没考虑过使用函数来实现消息检查者,我更喜欢它有对象的感觉一点,OOP语言嘛,而且类是肯定比函数有大得多的灵活性的。

【有些不需要id,wp,lp的checker该怎么办呢……需要比id,wp,lp更多参数的checker又该怎么办呢……】
之所以不需要再需要新的成员数据,是因为消息本身就只有那几个数据。如果需要检查更复杂的情况,比如说字符串,这样的模式也是可以胜任的,以前实现过。在 MessageChecker 当中加一个多态的 Data 成员,它的多态复杂度要比 MessageChecker 本身在堆上分配的小得多,目前没遇到id,wparam,lparam 三个参数解决不了问题的情况,暂时不会增加这个 Data 成员。

【从msvc和gcc的实现来看,虚指针是切割不掉的,它在父类中。虚表就更不存在切割一说……】
这样走旁门左道是因为以前确实遇到过虚函数失效的问题,应该不会真是我忘了用指针调用了吧?更可能是编译器bug,因为指针调用虚函数从小就记得。VC71对于C++标准的支持不是很理想,遇到过很多编译器报“cl.exe 内部错误”,特别是涉及模板的时候。

【是因为这样可以多态调用,但不能保证子类的不变式……调用的是子类的函数,使用的是父类的数据(被切割了)……】
什么不变式要变式哦,听起来好讨厌,能抓猫的就是牛老鼠。写出来的程序编译通过链接成功,客户能运行它的时候觉得心里爽就行了,所有的规则应该是为这个最终的目的服务的,而不应该为规则而规则。

【“for (_HandlerMapIter it = m_map.begin(); it!=m_map.end(); ++it)这个效率是很低的……HandlerMapIter的命名也是不符合c/c++规范的…… 都被Gof带坏了……】
我倒是并不觉得它的效率很低,因为只是十多次的循环,不过也希望看看你有更有效率的方法。命令风格是被翻炒了千亿次的问题了,使程序员从编码到设计都能保持最大的个性,我觉得正是C++的一大特色。就算有人腰上围一圈炸弹手执打火机来逼我改风格,我依然视死如归地觉得自己的命名方式美妙绝伦无以伦比的,坚持风格得有血可流有头可断发型不能乱的勇气啊。所以你就将就着看了。

“规则,风格,效率”,OwnWaterloo兄弟啊,你就是典型的传说中的学院派 cpper 。很高兴又来占我沙发,同时很是抱歉,这篇博文可能让你失望了。但我会再接再励,希望后续的文章能引起你的兴趣,不负你两占沙发的厚望。

@OwnWaterloo

你的这个需求有点像 java 的 AWT 的 添加 listener 的方式
addWindowListener ( new WindowListener(){
  void windowOpened(){ ...... }
  void windowClosing(){ ...... }
} );

也有点类似自动化脚本的
onclick = "javascript:{......}"

或者 lua 的
window.onClick = function( arg )
begin
  --......
end

确实是很诱人的语法。可惜三种 C++ 都不能实现,boost::lamda 生成的函数对象好像是临时的吧?也不能这样保存起来以后再使用。

@OwnWaterloo

你说:“如果在修改wc并注册之前,已经创建了一些窗口呢?
会怎样? 也会跟着被修改? 不会吧……?”

确实不会。Windows 还没实现那么神奇的功能,而且没必要。如果需要同时修改已经创建的窗口,就只能列举出来一个个地子类化。

@OwnWaterloo
你说:“超类化?”

随手找了篇文章:http://hi.baidu.com/combojiang/blog/item/45e968b7b8a510f131add11c.html@OwnWaterloo


你说:“跟是否可以函数对象没什么关系。主要需求是定义这个回调(或者函数对象)的地方最好和使用这个回调的地方比较接近。”

我的意思是,可以定义函数对象代替函数来作为回调。实在是需要纯粹的回调函数,也可以在外面定义一个公用的模板,然后在内部定义一个函数对象为参数来具现化这个模板,就获得了真正的函数地址。不知这个是否与你的需求有所出入?

@OwnWaterloo

你说:“已经注册过的窗口类,如果要塞context,都需要subclassing。
SetWindowLongPtr( hwnd, GWLP_WNDPROC, insert_context );
然后在insert_context中将context塞进去,SetProp或thunk。
只是GetWindowLongPtr塞不了。”

可以塞的,只是需要超类化(superclassing)之后再塞。子类化(subclassing)在这里不行。但超类化是更需要谨慎使用的东西。


你说:“如何在调用点上创建这个回调函数,而不是在调用点所在函数外。“

C++不能创嵌套函数的,但可以在函数当中创建一个函数对象。你试试看?

@OwnWaterloo
你说“那这个成员函数g,就不能在另一个线程中使用。因为table = get_tss_table();是线程相关的。”
首先用表肯定是可以的,比如说我们一个全局表。针对映射表的操作有三种,一种窗口创建时的插入,一种窗口销毁时的删除,还有就是窗口消息来时的查询。创建和销毁是在调用的 GUI 线程当中进行的,做不到跨线程的创建和销毁。而查询时是在 WNDPROC 中进行的,这个函数是由系统调用,也是在 GUI 线程中运行的。既然所有的操作都在同一个 GUI 线程,那何必要用全局表呢,还要加锁,这不正是 TSS 派上用场的地方嘛。

你说:“GetWindowLongPtr的其他限制”
GetWindowLongPtr 确实有比较大的限制。除了你所说的,对话框,按钮这类的已经注册过的窗口类,其 cbWndExtra 都已经是固定的,要想使用 GetWindowLongPtr 来存取额外数据的话,就必须要超类化,这样的就又麻烦了。所以综合考虑,SetProp 和 thunk 是最优选择。

你说:“总之…… win32 gui是很浩瀚的事情…………“
当然是的,不浩翰就没有辟波斩浪的快感嘛。OwnWaterloo 晚上两点还坚持在前线?深更半夜的,都在研究些什么高深课题呢?多好的睡觉时间啊,晚上睡得香,白天不嗑睡,早起早睡效率才更高!

@OwnWaterloo
你说:“性感诱人吧? 所以我极力推荐这妞……
也正因为误解很严重……
所以用户代码访问 [0, length ) 的几率还真不大……”

确实非常性感诱惑人,我们不要宣扬了,好东西我们两知道就好了,哈哈!MSDN真是有点猥琐。。。实现这么好个功能,却不知道大书特书,让那 GWL_USERDATA 忽悠了不知道多少程序员。OwnWaterloo 兄弟,该睡觉了,身体是革命的本钱啊,再聊。

@OwnWaterloo
你说:“我一直都在强调……
GetWindowLongPtr, GWL_USERDATA是普遍被误解了的……”

哈哈,原来如此。看来我对 GetWindowLongPtr 真是有深深的误解!和你一番讨论挺有收获的,这样看来我得再多权衡一下了!这样的 GetWindowLongPtr 是一个诱人的选择。看来不看书,只听道听途说来的东西真是不行的。可以回头去睡觉了,多谢了!

@OwnWaterloo
你说的:“
oid f( CWnd* w)
{
CWnd* d = w->GetItem( ... );
d->GetWindowTitle( ... );
}
具体是不是叫这个名字不记得了。
由一个窗口得到上面的控件的窗口,然后取一下标题。
如果w来自另一个线程 …… 等着程序挂吧……
d是一个指针,有主动释放吗?
mfc的消息循环还会在odle时清除这种"临时对象"的指针。
就为了满足它的table机制,需要在动态内存中创建对象,并使用类似gc的机制去清理……
mfc的大、慢就是这么一点一点的积累出来的。
table这种方式绝不可取……


你说的这个是 MFC 的实现嘛,MFC 还在系统中加了钩子呢。它的这个指针之所以有诸如线程的限制,也是因为实现的功能太杂。我们的 TSS 表可是压根没想过要实现这么个“临时指针”的功能。关于跨线程调用,应该是 API 是怎么样,跨线程调用成员函数也应该能干啥。TSS 表只是消息到窗口类当中很小的一步,不该影响到窗口类本身的工作。所以函数调用跟 TSS 表一点关系都没有的,如果一个函数调用因为 TSS 表而崩溃,那就是有问题的实现了。以前用过这种方式实现的,正是考虑到多线程才使用 TSS。

@OwnWaterloo
我可没说不考虑效率哈。别说C++,就算用石头写程序,都得考虑效率的。只是效率不是影响一切的标准,特别是当效率的差别微乎其微的时候。我不是不考虑效率,而是觉得用 thunk 实现 GUI 框架的效率不一定就能高多少,因为真正吊车尾的是消息队列, SetProp 和 thunk 这点微末的时间,相对系统得到事件,生成消息,翻译消息,发送消息,排队消息的这一大堆的内部操作的时间来说,短得不值一提。GUI 线程只是用来跑 GUI 的,不能用 GUI 线程来完成分步式计算啊。

用 GetWindowLongPtr 来作为实现框架确实也很简单,效率也最高。但因为GWL_USERDATAR 的众所周知而又独一无二,这个问题是很严重的,而且很明显,OwnWaterloo兄啊,何苦这么执着地为它辩护。。。。要是在广告中说道,“此 GUI 框架物美价廉童叟无欺,但请不要使用 GWL_USERDATA 因为本框架要使用”,怎么卖得出去啊。

三种方式当中 thunk 和 SetProp 确实是前者优一点,我个人放弃效率而选择标准一点的实现。至于 GetWindowLongPtr ,现在你就算用左轮手枪指着我的脑袋,我也还是坚持不能用的哈 。

@OwnWaterloo
就像你把火箭的点火系统装到拖拉机上,轰轰烈烈地点燃了拖拉机,它还是不能以宇宙第一速度脱离地球获得自由一样。Windows 系统维护消息队列的时间远远多于消息到窗口那一弹指一挥间,我们再努力地挤时间出来也只是寻求心理上的安慰。所以在写 GUI 框架时我尽量避免的是空间消耗而不是时间。

不过最佩服C++程序员们的一大优点就是,就算只有一点效率也是要打破脑袋挤出来的,有总比没有好嘛。你们讨论的我都认真看到,在效率和安全上 thunk 确实是很明显的最佳选择,我在考虑以后用这个了。在利益的诱惑下,心里的负罪感是可以克服的!

还是不敢选 GetWinowLongPtr 。它的好用是世人皆知的,就像一个大美女,人人都想染指一把,很难说在哪个月黑风高的晚上,哪个不爱看文档的程序员就忍不住用了它,于是崩溃蓝屏,整个世界清静了。。。这样的事一定会发生的,因为不爱看文档的人太多了。

SetProp 虽然也有危险,但是那概率小得多。世上的 Windows 程序员有多少,这些 Windows 程序员当中写 GUI 的又有多少,写 GUI 的程序员们直接用 API 写的又有多少,直接用 API 写的用到 SetProp 的又有多少,用到 SetProp 偏偏又用到和我一样参数的又有多少呢。我感觉遇到这样的概率比走路时被不明飞行物砸中的概率还要小。

当然作为一个库的作者,不能把完全安全性交给概率去解决。不像 GetWindowLongPtr 你只能眼睁等着崩溃,用 SetProp 为了增大安全性可以有很多手段。可以用一个 GUID 来 SetProp( guid .... ) ,可以用圆周率3.14159265。。。。,甚至可以用对女朋友的爱情宣言全文,爱是独一无二的嘛!

MFC的窗口指针可以在线程间传递,只是除了 SendMessage 之外能干的事不多。这跟那个 TSS 表没多大关系,GUI 很多 API 本身就是不能跨线程。真正不能在线程间传递的是 TSS 表本身,我们用 TSS 的目的就是避免线程间的牵扯,当然是不会在线程间传来传去的,而且这个表对 GUI 框架的客户而言是看不到的,想传也传不了。TSS 映射表的速度也不如想像中的慢,一个再巨型的 GUI 软件,能有多少窗口可供映射的呢。

这些方法我都用过。权衡起来,还是觉得 SetProp 是最简单好用的一个,有兴趣的同志可以测试一下在 SetProp 和 thunk 的实现效率差别有多大。我个人觉得在消息队列吊车尾的情况下,差别不大。

@Loaden
thunk 完成的功能只是消息从系统到窗口类的很小一步,也是最需要完成的第一步。在这一步上我尝试过很多方法,包括这种 thunk 技术,甚至还用过TLS做映射。我目前用的方法很简单就是 SetProp,GetProp 两个函数,有点重剑换木剑的感觉。效率上肯定没有 thunk 的直接调用高,但是心里更踏实一点,在内存当中写二进制代码总有在犯罪的感觉。

“过早的优化是一切罪恶的根源”。基于这一步只是整个GUI框架当中是很微小的一步,几乎不影响框架设计,可以先把框架搭好,再来从安全,效率,可移值性各个方面进行考虑。反正不要选择 GetWindowLong ,GWLP_USERDATA 那一套,如果发布出去后客户也使用这个东西就一切全完了,“过时的悲观毫无益处”。

你的消息封装看起来很舒服,肯定在 GUI 框架上也是下过很多功夫,喜欢重复制造车轮的的同志,我对这个兴趣也比较大,希望以后能多与你多交流互相学习,革命路上并肩携手一同进步!

@XML
那个代码找不到了,不过实现还记得,以后会写一下实现。
@null
读书千遍其义自现啊,最主要是闷着看代码。
从示例代码,最高层开始往底层追溯
@OwnWaterloo
模板有一大优势就是,不必在意类型是否已经存在,就能够任意调用它的任意成员。这是用虚函数也达不到的,因为虚函数也至少需要提供接口声明。
ATL这种方式,可以在不知道子类为何物的情况下调用它重写或者覆盖的成员。有时候可以完成用虚函数也无法达到的效果。
你试试不用虚函数,不用ATL的这种编译时的多态手法,在不知道子类为何物的情况下,在基类当中调用子类的方法试试?

@OwnWaterloo
我的理解是所谓的ATL-Style,其实是用模板在编译期手工模拟的一种多态,ATL 的实现当中大量地使用了这种方式,目的就是为了轻量级,几乎没用过虚函数。
我觉得这种手法的作用不仅仅限于此,因为可以结合其它的编译期技术,实现很多虚函数难以达到的功能,我实现 GUI 框架的时候也用到很多这种东西,以后的说明中应该会遇到。

@OwnWaterloo
他少打一个字母!done
@陈梓瀚(vczh)
你说“我跟OwnWaterloo就借着你的地盘版聊了哈”

绝没问题,欢迎有自己思考的同志来此版聊,如果能切紧GUI框架的主题就感谢了哈,你们的讨论能使我的博文增色不少。聊完不要走,此处管饭。

@侠客西风
呵呵谢谢你能喜欢,其实我对技术的探求不是很深,只是迷恋于一些更简单更表面的东西。做技术的人其实这样不是很好。
你就不要准备了,直接把我加进去吧。但我不知道怎么加友情链接,在后台加过好像没成功。
很高兴认识你!

@矩阵操作
谢谢,很高兴又认识志同道合的朋友。以后请多指教!
@OwnWaterloo
@陈梓瀚(vczh)
讨论了那么多,突然发现怎么两位同志的观点都扭转了?
“C++中实现属性”这个问题也是讨论过千百遍的了,而且注定是谁都无法说服谁的问题,因为它不纯粹是一个技术问题。很大程序上和性格喜欢相关。就好像实用主义点的程序员,肯定觉得这样费尽力气去模仿不值得,但艺术气质一点的程序员,觉得属性的语法看起来很漂亮。然后各自用自己的偏好去说服对方,肯定不能成功。
我个人的观点是,有总比没有好。存在即合理,要不然怎么会那么多的语言提供语言级的属性支持了。C++当中比这个急迫要解决的问题还很多,但可以预见,属性这东西在未来的某个C++标准当中一定会出现的,它确实有着不可磨灭的价值。

但你们的讨论里各自的观点都很有道理,里面包含的有技术含量的思考很多,我看过也很有收获。所以请你们在友好和谐的气氛当中继续尝试说服对方吧,无论是什么样的讨论都是思想的碰撞,也是学习的很好的方式。

@OwnWaterloo
谢谢你分享你的思考,真的很有创意。建议你可以把你的思考写到你的博客里,让更多的人看到。

@陈梓瀚(vczh)
高效的,不会制造麻烦的东西,也是从不高效的,会制造麻烦的东西进化来的。所以我觉得只要在思考都是有价值的。
@空明流转
好的,是这样的,《2012》说的是地球毁灭的故事,好了我说完了。
哈哈!
@OwnWaterloo
你说“我想到一个几乎没有消耗的方案……我再完善一下……”
加油,出来了别忘了写出来大家分享。
@OwnWaterloo
你说“我去看了看Imperfect C++。因为是英文的,以前只看了一部分就停了……我觉得前面都写得不错,ABI、mangling这些问题确实是很困扰……我继续去看书中怎么实现的……”
在C++里面属性只是看起来很美的东西,其实用性和想像的有差距。你说得对,用方法能实现所有的功能,所以我的GUI框架已经去掉了属性支持。

你说“我以前了解的实现方式,每一个proxy至少得带一个指针……这消耗还是蛮严重的……”
不一定得带指针的。比如《C++ Imperfect》里实现的属性是不需要带指针的,但它不被C++标准支持,应用有限制。我也实现了不需要带指针的属性,同时也是跟C++标准相容的,以后会说一下这个实现。
@OwnWaterloo
你说“没有完整的,就是你blog上发的一些片段……”
我找找看,找到了也给你一分。那些片段隐藏了实现的,看不出什么有价值的东西。
@WXX
你说“如果单就消息的派发,那么一个gui框架可以很容易实现,但是要很好的管理窗口之间的关系,很好的处理键盘加速键,鼠标等这些交互就麻烦了,考虑消息的过滤等。”
各有难处,但我觉得后者更容易一点,以前我也都做过一些。我觉得好的框架应该是在所有东西之前的。先有骨架,然后再说高矮胖瘦。长得是否高大健壮,阳光帅气,就先看骨架如何了。
@OwnWaterloo
你说“哦 原来是property模拟。”
嗯就是这样的。

你说“这个代理对象也是要占空间的吧?”
理论上来说不要占空间的,但实际是占用一个字节的。因为C++标准规定不允许存在0空间占用的成员变量,因为那会造成 &object.member1,&object.memeber2 两个地址完全相同的情况。
@OwnWaterloo
你说的“接下来,框架将message 分派到onXXX之后,客户再将onXXX转发(forwarding)自己的handler这个过程,我相信是可以编译时确定的。
——因为我看过你以前的一些实现~_~”

你说的这个是以前另一个版本的框架了,跟这个完全不一样了哦。你看那份代码,它的消息映射是编译期自动进行的,映射者,检查者,分解者三个角色都是由一个东西全部完成的。我将写的这个版本不一样,是完全分离的实现,没有那种编译期映射的功能,但运行时映射可以获得更大的扩展性。

可惜啊,以前那个版本的框架代码我已经找不到了。你那里有?

你说的“以我很好奇window是如何零成本完成映射与转发,并且是一个空对象的。映射肯定需要在某个地方做,可能不是window。运行时可更改转发目的地而不使用数据, 好像…… 太不可思议了……”

看来你有点误会我的意思了,肯定内存当中是有一个 std:map 之类的映射数据存在的。我说的0成本指的是 window.onCreated 这个成员的实现。
@OwnWaterloo
这个消息映射者的实现没有数据成员,没有虚函数存在,它其实就是一个调用,所以它也是有时间成本的。是的,所以我说是接近0成本,而不是真正的0成本。毕竟世界上没有那样传说的员工。但如果好的编译期可以轻松优化掉这个小小调用。

你说不可思议,那倒没到那个境界哈。你可以看看《Imperfect C++》当中的method_property的实现,跟那个很类似,不过他的实现不符合C++标准,应范围有限。

用这种消息映射者的方式,我也实现了值主义的属性,比如:
window.size = Size( 100,100 );
Size size = window.size;

从理论上来说,也接近0成本的。
@OwnWaterloo
谢谢沙发,后面会详细说说这个消息映射者的实现
@stidio
这个设计是设计给不跨线程的应用的,主要考虑在这种应用下,如果线程太多,都去查询同一个全局的东西,大多数线程都一直处于等待资源的状态,比较浪费CPU时间。
这是在公司时写的哈,出来后倒没写了,感觉是人越来越懒了,写程序越来越没激情
以前也研究过逆向,现在想来还真是有精力
整天在黑漆漆的反汇编里转
还是C++的世界光明啊
http://www.pediy.com/bbshtml/bbs8/pediy8-266.htm
http://www.pediy.com/bbshtml/bbs8/pediy8-260.htm
我觉得这种有点奇巧淫技的意思,不过调起BUG来真方便。多谢!
re: 垃圾收集的那点事(J) cexer 2008-09-23 17:40
@LOGOS
多谢了,我也研究研究。
re: 垃圾收集的那点事(J) cexer 2008-09-23 10:32
哪里有下载的源码
多重继承的时候,使用C风格的转换,可能会出乱子。
re: 甘特图1.0.0β发布 cexer 2008-09-07 17:43
well done!
template<int i>
struct sum
{
enum{ result=sum<i-1>::result }
}

template<>
struct sum<1>
{
enum {result = 1 };
}

result = sum<n>::result;
re: GUI Preview Demo完成! cexer 2008-08-26 13:35
@陈梓瀚(vczh)
是的,那东西还要再修改一下。
re: GUI Preview Demo完成! cexer 2008-08-26 10:35
鉴于CPPBLOG上批判者多于探讨者,炫耀者多于分享者,陈同学分享代码是一个不容易的决定,谢谢并支持!!
re: KWinGUI的一个DEMO cexer 2008-08-26 10:34
我那个消息机制我自己已经放弃了,不过还是愿意与你探讨探讨。加我的QQ41086722
@proguru
最近就会写的。
@陈梓瀚(vczh)
不过如果你不接受类成员指针的话,那么你还是接受interface吧。仅仅接受一般函数会丧失很多能力的。

我写这个日志的主题在于实现一个通用的消息映射的数据结构,至于消息处理函数是是成员函数或自由函数,不在讨论范围内,并且那个很简单,也不用单独写篇日志。
@陈梓瀚(vczh)
现在只是封装映射的前半部分,下一篇会封装消息处理函数的类型。
@LOGOS
为什么说用一个for是不行的呢?这儿只是个示范。不一定非得用map。
共4页: 1 2 3 4