All Stories

基于Lua的可扩展架构设计

  首先,这个架构是一定程度上模仿Eclipse的,并做了大量简化,所以模仿得并不像,不过不得不说最原始的灵感和很多想法是从它那里来的。  支撑起整个可扩展架构是一个类,大概名叫pluginsRegistry的类。这个类一般可以使用singleton模式,它做一些与插件相关的最基础最底层的事。比如,在宿主程序(这里一般是一个C++程序)启动时,它负责扫描指定的一个目录(包括子目录)下所有文件,找出其中的plugin.xml文件,也就是插件描述文件。它并不一定要是xml来描述,也可以是用Lua来描述,毕竟Lua最早的用途就是作为配置文件的,而且这个文件并不会被程序动态修改,Lua也够用,但要注意的是名字空间污染的问题,一个办法是每个描述文件都使用完全相同的结构和变量命名,但每次都单独启动一个Lua解释器来解析。一般说来一个描述文件中声明一个插件,也就是一些基本的插件信息,包括插件id、插件扩展点和插件对应脚本文件路径这三项最主要的不可或缺的信息,以及一些诸如被扩展点,版本号,依赖关系,版权信息等等不那么重要的信息。扫描到一个插件后,这个类会把插件id,脚本文件路径对应着保存起来,并把该插件添加到指定的被扩展点上。所谓扩展点,其实准确一点说,应该是被扩展点代表一个插件会被激活(其中一部分)的时机。被扩展点一般也是由其他插件提供的,也就是说其他插件会在某个它认为合适的时刻,激活所有注册到被扩展点下的所有插件。所以插件描述文件中很重要的一点是,需要声明自己是扩展了哪个被扩展点,不然就永远不可能被激活。由此可以看出,另外一个需要注意的是,一个插件一般会依赖于另外一些插件,至少是依赖于提供它要扩展的被扩展点的插件。一个插件必须要指明它要扩展的被扩展点,这是别的插件提供的,另外它也可以提供自己的被扩展点,让另外的插件来扩展自己的功能,当然这不是必须的。为了说起来不那么拗口,在描述文件中,一般我称前者为扩展点,称后者为被扩展点,这是把同一类事物在一个所属中以其不同的功能或角色来区别而得来的。再回到这个类的功能上来,除了前面讲的这几点功能外,它还应该能向外界提供一个服务,通过该服务外界可以根据指定的被扩展点查询到所有注册在该被扩展点下的插件,理论是希望只是通过一个id就能唯一标识一个插件。这样每当时机合适时,提供被扩展点的插件就可以通过这个服务来做一些事情,最常见的是通知所有该被扩展点下的插件即可,那些插件自己应该清楚这时应该做些什么事情,还有种情况是,提供被扩展点的插件应该可以从中区分出一些插件,而不是全部,来通知。一个很常见的应用场景,主菜单,可以是一个菜单项对应一个插件,这时每当用户点击了菜单项,那么只要激活该菜单项对应的那个插件即可,而不是所有主菜单下所有的插件都要激活一遍。  通过前面的阐述,可以知道,这个类需要一个清晰简单的接口,这样才可以比较方便地既给C++代码提供服务,又给Lua脚本提供服务。尽管Lua从设计上就考虑了很多跟其他语言交互的情形,但这里我还是得小心一点,少使用一些只有某种语言特有的一些元素或特性。  最后,以一个实例来简单叙述一下这个架构是怎么真正让一些功能运作起来的。就还是以主菜单为例,主菜单也是一个插件,当然,它是用C++还是用Lua实现的我们不关心。重要的是主菜单插件首先有一个自己的id,并提供了一个被扩展点,假设叫view.ui.menu,这时假设那个类已经把所有扫描到的插件正确地分析过了,这个被扩展点下已经有一些插件了,比如file_open,file_save等等。主菜单插件在自己被初始化时,调用那个类来得到所有view.ui.menu下的插件,并依次激活这些插件的get_caption方法,根据这些个方法返回的内容,作为菜单项的标题,逐个添加菜单项,并把菜单项的id和它对应的插件(可能就是用插件id来表示)保存起来,当用户点击一个菜单项时,主菜单插件可以得知用户点击的菜单项的id,并从之前保存的信息中得到它对应的插件id,以此来再调用那个类来激活唯一对应的那个插件,那个插件知道用户是点击了菜单点,就执行一些操作,比如打开文件等等。  大致的流程就是这样。

西安出差

  昨天晚上6点半的飞机,回到深圳住处已经10点了。在西安过了6晚,有4晚出去转了转,给我印象比较深刻的是那里的吃的和人文景观。  22号傍晚到的西安,结果最郁闷的是被人忽悠了,居然有两个名字一样的酒店,而且我明明说得很清楚是哪条路上的,结果司机还是把我带到了另一个酒店那里,太扯了,比国产007还要傻冒。马上出来打车走,结果说明了是东仪路,那个司机硬是开到了东一路,我大汗!好不容易啊,才到了最终目的地。一个小小的窄窄的地方。放下东西,去酒店附近一个砂锅店吃了些东西。第一天,给我最大的感受是,西安美女挺多的!在的士上时,就看着路上的行人中,很多漂漂的mm,后来去砂锅店,就又看到一个很漂漂的mm一个人在那里很优雅地吃着粉丝。  23号晚上,听从王同学的建议,去大雁塔喷泉广场转了转。到了那里才郁闷地发现,这明显是个情侣才来的地方啊。可怜我一个人,天又冷,好凄凉!随便走到路边的一个店里,买了点东西,回去哄一下家里的大小美女。其中一条街上,一家一家的小店,叫百工坊,都是当地特有的手工艺作品,主要特色是可以让游客DIY,但是我看到基本没有什么人进去,不知道是不是因为已经是晚上的缘故,或者是天气冷,游客不多的缘故。正当我无所事事准备回酒店的时候,广场广播突然说有什么水舞表演,于是好奇地停下来等。水池旁边站满了人,中间也有很多人,我挤进一个地方,原来就是喷泉配合音乐,喷出不同的水柱,所谓水舞。不过也是第一次看到这种东西,还是有点新鲜感的。  24号晚上,我本想去买些玉的,因为蓝田就在西安附近,所以西安就有很多卖玉的,问了下王同学,她说去书院门买。结果等我打车到了书院门,那里的店铺都已经关门的了。没办法,于是只好走到对面去看城墙,反正王同学也说,这好歹也是西安一景啊,哈哈。门票40,我不清楚这种行情,直接进了里面。刚好有什么大唐灯会,于是在城墙上走了一两个小时,天还是很冷,回!  25号晚上开始下雪了,而且从公司出来打车,居然打不到,走来走去,过了一个小时才打到,汗!直奔回民街,跟司机聊起,他居然是上海人,一下就猜出我老家是哪儿的,哈哈。到钟鼓楼下了车,绕了一圈才发现那条回民街,真是条小巷子,而且人不是很多,我不知道是因为天气不好人少了,还是因为已经有点晚了人少了。走到另外一边,时代盛典那里,也有一条回民街,人更少,店都关门吗。没办法,打电话找王同学确认一下,王同学也跟我说不清楚,不知道她哪里找来另外一个mm来跟我说,我才明白,第一次进去的那条小巷子是对的。于是对跑过去,吃了笼贾三灌汤包就很饱了,晃悠晃悠晃到另一边,随便找了个泡馍店,进去点了个牛肉泡馍,不得不说,这味道真的比公司食堂的好太多了,肉嫩味美。  26号,下大雪了,早上起来发现屋顶上,车顶上都积了几厘米厚的雪,白天也是飘着鹅毛大雪。只好不出去了,老老实实呆在酒店里看小说看电视。  27号,想着第二天就要回深圳了,下了班立马又跑到回民店买些吃的带回去。顺便去吃了下之前那个mm推荐的红红炒米,感觉跟蛋炒饭差不多,不过是里面有些肉丝和酸菜。又叫了几个羊肉串,还有不知道是什么肉串,味道都不错,至少这羊肉串我是比较得出来的,比起其他地方吃过的,这里的特别嫩,而且膻味不大。吃完后,晃悠了两圈 ,进到一个大一点的店铺里,买果脯,就挑那种平常在深圳不太有得买的,我也不知道价格上有没有问题,不过想来再亏,数量也不大,也就不去计较了。  偶尔去下这种从没去过的地方,还是挺有意思的,不过一个人毕竟乐趣就少了些了。

今天去西安

  一周前就被告知要去西安出差,心里还是有点忐忑的,说起来有点丢人,还是第一次一个人去一个陌生的地方,真正的人生地不熟啊,哈哈。以前去重庆,有同学一起,来深圳,也有校友一起,去贵阳,有表哥和小妞接待,这次可是真的一个人咯。

终于解决Lua中任意使用C++对象

  仰天长啸三声,哈哈哈!一直没想出或找到什么办法可以比较方便地解决这个问题。我的需求是,在C++中随时可能生成一些对象,而这些对象随时可能需要被Lua获取并进行一些操作。  在网上转了很久,看过LuaBind,还有一些文章。只有LuaTinker有一个可以把C++对象映射到Lua全局变量的方法,似乎有点接近了。但实际上离我的需求还是有点距离,比如它需要由C++代码知道这是给Lua用的,并主动把对象映射过去,而且是映射到Lua的全局变量中,通用性不够。我希望C++代码可以不跟Lua交互,其次,应该由Lua主动去获取它需要的C++对象,这样Lua可以自己决定把这个对象保存到什么地方。  今天又看了一下SWIG的文档,发现SWIG对Lua的封装做得实在太好了,它可以封装出自定义类型的指针,例子代码中以FILE *作了示范,想了想,我在C++中写个函数,返回相应的对象指针不就行了!试了试,果然可以,这下终于搞定了,可以继续写编辑器了。昨天还在想,有一部分Scintilla的初始化工作,代码好长,虽然初始化内容是保存在配置文件中的,但是配置项多,而且互相之间比较独立,C++代码仍然需要一个配置基一个配置项地写,我就觉得这部分应该交给Lua去做。这里Lua的功能除了原有的保存配置的功能外,还兼任了使配置生效的责任,而且仍然保留了原来配置文件的不需要重新编译主程序就能修改行为的优点。这个思路是对的,但技术难点就是Lua如何操作那个编辑器,这下好了,没问题啦,SWIG真是个好东西,还要Luabind之类的C++ Wrapper干什么!

我真是个换肤论者

  之前说到,用多了chrome,竟然不知不觉地想着firefox的界面也想跟chrome一样,说干就干,先去找theme,要能尽量模仿chrome的,还真找到一个chromize extreme,不过它只能支持3.1b1以上版本,没办法了,我现在是3.0.6,只好去重新down了一个3.1的装上,还是像以前一样,用命令行参数,将配置目录指向3.0.6的目录下,这样就可以使用3.0.6中已经有的一些东西,不过有些扩展不兼容,暂时不管了。装了chromize extreme后,挺像chrome了,除了多了个标题栏。找到一个hide caption扩展,不过发现它不能用在3.1版本中,于是另外想办法。最后只好用custom button扩展,加上一段脚本,叫max的脚本,会自动将firefox弄成最大化并隐藏标题栏。  搞定!界面看起来真的像chrome,感觉舒服多了,哈哈,就是不能用3.0中的一些扩展了有点遗憾!

傻掉了

  突然发现,怎么Firefox有标题栏,怎么有菜单栏,怎么这theme就不正常了呢!使劲点了几下菜单,也没变化,切换了一下theme,重启firefox,菜单栏不见了,再切换回去,怎么标题还在!  突然想起来,这标题栏似乎一直都在的!是chrome用多了吧,傻掉了,汗!

黑社会?

  8点钟才从公司出来,坐上B666回住处,也不算太晚。快到小区门口的公车站时,我已经站起来走到门边,一辆SUV却超上前拦住了公车。从SUV下来五六个30来岁的男人,都是圆寸头,拍开驾驶室的窗很嚣张地对司机说,叫他以后不准再开,要是明天再被他们看到要怎么怎么的。这样捣鼓了几分钟,终于走掉了,我还一直担心他们会不会上车来抢劫。  记得从上中学开始,每当说起以后娶妻生子的事,我就有点烦躁和恐惧,我总是担心自己的小孩没教好,变成流氓。我妈知道我的想法后,曾也和我说过,只要好好管教,是会好的。今天看到这些人,我又想起这些来,不禁又想,这些人的家人都是做什么的,他们有没有父母,有没有妻儿。  这个世界,真让人憎恶。

开始使用wxWidgets进行界面开发

  半生不熟地用着wxWidgets,全是照着代码改的,勉强能运行起来,不过似乎用gcc编译出来的release版本有时候会报错,不知道是哪里出的问题!可能是哪里资源没正确释放什么的吧!  界面的框架算是搭起来了,排除掉那个报错的bug就比较完善了。接下来完成插件扩展机制,一大块功能就可以用脚本完成了。

差不多封装完Xerces-C++了

  其实只是封装了其中很小很小的一部分,DOM的那部分,DOM中DOMElement相关的那部分。接口是模仿那个同事的MSXML封装类来的,应该说,只有最初的灵感是来源于那个MSXML封装类的,后面真正用得最多的部分,实际上已经是被我修改过的。其中最有用的部分是,对属性和文本内容的设置和获取,针对不同的数据类型作了特化,以及对NodeList部分增加了iterator来适配STL算法。  对于这个封装的意义,或者说价值到底有多少,自我感觉,至少在现在手头这个项目中,在boost::bind、boost::lambda配合STL算法的联动操作下,让代码少了好些,不过也许带来的问题是,可读性变差,以及不再去思考是否有其他更好的方案的惰性增加。