Linkman的学习记录

学习记录,兴趣方面:实时数据库、MES、SIS、工控软件、C++编程、人机界面、嵌入式软件、可视化等

VC知识库BLOG 首页 新随笔 联系 聚合 登录
  100 Posts :: 16 Stories :: 403 Comments :: 0 Trackbacks

留言簿(17)

随笔分类

随笔档案

文章分类

文章档案

传说中的名人

我的链接

朋友

搜索

最新评论

阅读排行榜

评论排行榜

   VCF是一个C++编写的开源项目,全名Visual Component Framework,中文名:可视化组件框架。在这里先把我这两天的学习体会记录一下。
   程序框架库应该可以算是系统级软件,它提供给用户一种相对固化的编程方式,进而简化系统编程。想想最开始在Win3.1下使用SDK开发程序的日子,我们真的应该感谢编出这些框架库的英雄们。
    最早且最流行的框架库应该是Borland公司的OWL库(早期的Borland公司推出的每一样东西都是极品)。再后来也是目前最流行的框架库就是MFC了,C++Builder中的VCL库也是非常有特色的。最能评价MFC和VCL的好坏的人,应该是侯捷,他分别写了《深入浅出MFC》和《深入浅出VCL》,对每个库的分析都是非常不错的。
    目前,随着计算机技术的发展,越来越多的人们对计算机的熟悉,框架库的数量和类别也越来越多,及外延也越来越广,以至于在某时,我们不知道某个库算不算框架库。就象前几天分析的WTL(它也变成开源项目了),以及目前名声在外的STL、BOOST、WXWINDOW等。他们也应该属于框架库吧。
    还有三个软件是非常值得一提的:
    1、AGG,一个类似于GDI+的2D图形库,写得非常的好,如果有空,我应该将前段时间分析AGG的经验写出来。
    2、LOKI库,就是那个写《C++设计新思维》的作者提供的,如果有空,我应该将前段时间分析LOKI的经验写出来。
    3、CPPUNIT库,一个单元测试的工具。
    谈了这么多,还是重点讲一下VCF本身吧。
    我看重并分析VCF的原因,应该是其RTTI功能,其次是它采用了比较先进的C++编程手法。
    由于我正在编写一个开源的工控软件,想编写一种通用的属性处理程序,编了很长时间没有进展,后来仔细分析了一下我的需求,才发现我想要实现的功能,其实就是一个RTTI的扩展功能,何不直接找一个具有RTTI功能的库来分析一下呢?于是上网查找,发现VCF对RTTI的实现是最完整、最符合我的需要的。而其它库,对RTTI的实现,要么不完整,要么实现方法太差。
    RTTI的实现,首先应该看语言本身对其的支持程度,象Delphi、C#、VB等语言本身就有Property属性的,对RTTI的实现相对就简单的多,而C++语言本身无支持Property的语法,而在CodeProject上看到的一些实现方法,都不是太好。
    昨天花了一晚上将VCF的帮助文档看完,并分析了其RTTI的实现方法,体会如下:
    1、由于VCF还处在开发阶段,其帮助文档很不完善,有许多内容都是空的;
    2、VCF的源码则非常清晰,不愧不大家手笔;
    3、VCF对RTTI的实现,包含在FoundationKit的RTTI目录中,包含了十多个文件,看其文件内容,其实现方法应该算是中规中矩的;
    4、VCF实现了如下RTTI功能:
        A、运行时查找对象所对应的类的名称、ID、描述;
        B、类的继承关系;
        C、Property功能;
        D、Event功能;
        E、Method功能;
        F、为属性编辑而实现的Enum功能;
        G、为属性编辑而实现的Item功能;
        H、运行时基于名称和ID的动态对象创建功能;
        I、Interface功能;
        J、运行时类信息的注册功能;
        等等。
    5、从功能上讲,这非常接近我的需求,但还差如下功能:
        A、基于属性的自动保存和读入功能(象Delphi那样);
        B、运行时提供属性的隐藏和恢复功能,虽然目前通过其运行时增加和删除属性可以变通实现,但实现起来不方便;
        C、属性的分层;
        D、重点属性的标注;
    6、另外,从其实现看,性能可能会是一个非常大的问题,因为其运行时对属性的访问都是通过名称来访问的,即便是通过UUID访问,也是通过UUID的字符串访问的,而实现上,在一个程序运行阶段,大部分类属性的访问是不需要通过名称来访问,而直接可以通过序号或编号来访问的。如果我想将它拿来用,一定要先对其性能进行重写。

今天分析完了VCF的RTTI相关代码,现将分析心得记录下来。
    在VCF中,与RTTI相关的文件如下:
    1、Property.h        属性的定义,包括事件属性的定义
    2、Method.h         方法的定义,包括方法的参数定义
    3、Field.h              字段的定义
    4、Class.h、Class.Cpp        类RTTI的定义
    5、InterfaceClass.h、InterfaceClass.Cpp        接口类和实现类的定义
    6、ClassInfo.h      定义类定义时的宏操作
    7、VCFRTTIImpl.h    类定义的具体实现类
    8、ClassRegistry.h、ClassRegistry.Cpp    全局性的插入和记录整个系统中类RTTI信息
    9、VariantData.h、VariantData.Cpp    定义VariantData数据结构,类似于VB的Variant变量
    10、Enum.h    定义枚举型属性的枚举

一、Property的功能分析
    Property提供一种对对象的读写操作方式,通过Property定义,用户可以采用如下方式访问对象:
        Obj.GetValue("属性1");
        Obj.SetValue("属性1",100);
    在VCF中,Property被定义为抽象类,其定义中包括如下成员:

protected:
    VariantData value_;  
// 属性的值
    bool isCollection_;  // 属性是否是集合类型,即属性是由其它值或属性组合而成
    bool isReadOnly_;  // 是否只读
private:
    
bool bound_;  // 属性是否已绑定到对象,对于已绑定的属性,每次改写都会引发属性改变的事件
    String name_;  // 属性的名称
    String displayName_;  // 显示名称
    String description_;  // 描述
    PropertyDescriptorType type_;  // 类型
    Object* source_;  // 属性对应的实现对象

    除了提供对这些成员的操作函数外,Property还定义了其它一些函数:

// 属性读写操作

virtual VariantData* get() = 0// 从对象中读取属性,被定义为一个抽象方法,由子类重写
virtual void get(VariantData* value) = 0// 住对象中写属性,被定义为一个抽象方法,由子类重写
virtual String toString(); 将属性的值转换为字符串

// 对集合型属性的访问操作

bool isCollection(); // 是否是集合型属性

virtual bool hasMoreElements( Object* source ); // 遍历集合中的值
virtual VariantData* nextElement( Object* source ) // 遍历集合中的值
virtual void startCollection( Object* source ) //// 遍历集合中的值
void add( VariantData* value ) // 改变集合集的内容
virtual void insert( Object* source, const unsigned long& index, VariantData* value ) // 改变集合集的内容
virtual void remove( Object* source, VariantData* value ) // 改变集合集的内容virtual bool collectionSupportsEditing() // 是否支持集合型属性编辑

    可以看出:
    1、Property只是一个接口类,其具体实现由继承子类完成,这是一种比较好的定义属性的方法,因为属性的操作、特别是属性的类型是很多的,如果在此实现Property的所有功能,将会严重限制Property的功能扩展;
    2、从对集合型属性的处理来看,作者对设计模式的理解是比较深的,此处可以看作是Composite模式的一种特殊应用,对集合属性的操作则使用了Iterator模式,且定义了一种对序列型容器的通用遍历方法;
    3、定义的value_是一个属性读写的缓冲区,即对同一类的所有对象,都是通过value_交换数据,value_被定义成一个VariantData,可以包含系统中使用的所有类型数据,但这存在一个问题,在多线程中,这是不安全的,不知VCF在程序的其它部分是否会对多线程模式下作特别处理;

二、EventProperty的功能分析
    所谓Event,即事件,事件包括三个方面:事件的发生者、事件的处理者、事件本身的数据。举例说明:在控件中,针对鼠标移动,可以定义一个"OnMouseMove"事件处理过程。在此,控件是事件的发生者,OnMouseMove的处理对象和处理函数是事件的处理者,移动的数据和是否按下Ctrl键等,则是事件的本身。
    在VCF中,EventProperty被定义为抽象类,其定义中包括如下成员:

protected:
    String eventClassName_; 
// 事件发生者的类名称
    String handlerClassName_; // 事件处理者的类名称
    String delegateName_; // 事件处理函数的名称
    Object* source_; // 对应的源对象
    DelegateMethod delegateMethod_; // 事件处理方法

    除了提供对这些成员的操作函数外,PropertyEvent还定义了其它一些函数:

virtual EventHandler* createEventHandler( Object* source, EventHandlerMethod method, const String& name ) = 0// 新建一个事件的处理过程

    由于未分析事件的实际例子,对其实际调用时机尚不得而知。

三、Method的功能分析
    所谓Method,即方法,提供一种对象功能调用的机制,定义了方法,对对象的操作可以如下进行:
    Obj.Method1(Para1,Para2);
    以下是Method的成员定义:

protected:
    String argTypes_; 
// 参数的类型定义,用一个字符串表示,字符串中的每一个字符代表一个参数的类型
    ulong32 argCount_; // 参数的个数
    bool hasReturnValue_;  // 是否有返回值
    String name_;  // 方法的名称

其中参数的类型定义采用字符表示:
      值    
|            类型
    
----------------------------
      
"i"   |        integer
      
"l"    |        long
      
"h"    |        short
      
"+l"    |        unsigned long
      
"f"    |        float
      
"c"    |        char
      
"d"    |        double
      
"o"    |        Object*
      
"b"    |        bool
      
"s"    |        String or String&
      
"e"    |        Enum*

    其成员函数只有如下一个值得关注:

// 执行方法
virtual VariantData* invoke( VariantData** arguments,Object* source=NULL ) = 0;

    总的来说,方法的定义比较简单,处理起来也算一目了然,但其定义参数类型采用字符表示,个人认为不太方便,完全可以定义成枚举型。参数类型也不需要定义为一个字符数组,采用一个长度为6的字节数组就够了,因为其在定义参数接口时,只定义了最多6个参数的情况。
    byte  ArgTypes_[6];

四、InterfaceClass的功能分析
    Iterface和ImplementedInterface分别定义了接口和接口实现,主要用来封装COM接口,比较简单,不再说明

五、Field的功能分析
    比较简单,不再说明

六、Class的功能分析
    待续......

七、类RTTI的声明过程
    待续......

八、类RTTI的注册过程
    待续......

九、VariantData的功能分析
    待续......

十、总体分析
    1、关于VariantData
        为了提供对不同数据的访问接口,VCF中定义了VariantData数据类型,其定义类似于VB中的Variant类型,但类型没有VB中那么丰富,且其对String是单独处理,个人觉得,这种对String的单独处理是一种不得已而为之的处理,因为在VCF中,String被定义为一种std::basic_string<w_char>的简单封装,没有自动回收机制,因此,不能与其它简单数据(如int)同样处理。但应该会有一种和谐的处理方法的,上次在网上看到一个名为CSDTString的类,它是对STL中字符串类的仿MFC的CString的封装,过几天要仔细看看,看是否能用在此处。
    2、关于RTTI的声明过程
        RTTI的声明过程,类似于MFC中对消息处理的封装,也是采用了一系列的宏,这样定义后,在类中声明属性和方法的格式如下所示:

BEGIN_CLASSINFO(Foo, "Foo""VCF::Object", FOO_CLASSID)
OBJECT_PROPERTY( Foo, 
"fooObj", Foo::getFoo, Foo::setFoo )
END_CLASSINFO(Foo)

        这样的处理有优点也有缺点,其缺点不是说其编写方式(采用其它方法,也许其属性的编写方式会更长更难看),而是指其宏展开后的内容。其展开后,是一个嵌入在Object中的一个嵌入类,这个嵌入类在创建时,判断类的信息是否已在系统中注册,如果未注册,则将类、属性、方法等向系统注册。我目前尚未找到一种更好的处理方法,但能明显看出其在处理时有如下缺点:
            A、类在每一次实例化时,都会产生一个嵌入类,虽然这个嵌入类是轻量级的;
            B、嵌入类在判断是否注册,是向系统查询,是否存在这个类的注册信息,但作为类来说,完成可以有更简单的方法来判断其是否已向系统注册过,只需要一个静态标志即可;
        另外,我好象在《C++编程新思维》一书中看到,所有具有这种处理形式的类,完全可以通过模板来实现,具体方法还要查查书。
    3、关于属性的改动事件
        VCF中对属性的改动事件,是通过一个嵌入的委托类实现,思想很好,但似乎有设计漏洞:
            A、只要属性被绑定到对象,则每一次set函数的调用,都会激发属性改变的事件,但如果set调用而并没有改变属性,这种事件是多余的;
            B、每次改变事件发生时,它都会将改变前的值和改变后的值通过事件封装包发送到观察者,但很多情况下,改变后的值并不是送进来的值,比如:设置某控件的长度为123,但控制在得到123时,可能会对经修改,以处理对齐等功能,这样,改变的值可能是125而不是123;
            C、属性的改变发生时,很可能会产生多个属性的联动,这种联动,如果采用每个属性的改变都会触发事件,则效率太低,如果不处理,则可能导致View和Document的不一致;
            D、作为工控软件,应该要考虑到效率,所以,对属性的改变应该要增加批处理的功能;
    4、关于String
        VCF中,将String定义为Unicode字符值,因此,可以对中文进行处理,但在系统中,大量的函数都如下所示:
                String GetName() { return name_;}
        注意到它返回的是一个对象而不是引用,对性能肯定会有非常大的影响。就其原因,应该是系统的设计者想将String类型的数据作为一个简单类型的值,与其它简单类型的值同样处理。统一是一回事,性能又是一回事,一个用户是否选用某个系统,他很可能就因为其运行速度不行,或是程序的尺寸太大,而否决这个系统(想起了我在最后一家公司,那个伟大的7.0系统)。
    5、关系性能
        我分析VCF的过程中,我对其性能有非常大的疑问,因为其对RTTI的访问,都是通过字符串的查找进行的。
        在一个典型的工控应用程序中,对RTTI的操作应该包括如下几个方面:
            A、在组态环境下,通过属性来改变控件的值,如改变字体、颜色等。
                这种应用情况下,系统是能准确知道那些类需要改变的(主要是选择列表),因此不需要通过对象的名称来访问对象。因此,在对象中,增加一个指向其RTTI信息的指针是非常必要的;
            B、组态环境下,通过RTTI,从文件中读取数据,直接生成控件列表。
                这种应用情况下,只能通过名字来查询,但应该可以通过一些特殊处理,对同一类型的对象只查一次表;
            C、组态环境下,调用对象的属性、方法、事件等
                这种应用情况下,对属性、方法、事件应该可能通过名称和序号两种方式访问,因此,增加属性、方法、事件的序号访问方法是必要的;
            D、运行环境下,调用对象的属性、方法、事件等
                这种应用情况的处理,决定了系统在运行时的性能,由于对象的属性、方法、事件等都是用户脚本程序自己编写的,用户在写脚本时,肯定是通过名称来处理的,如:
                窗口1.控件2.宽=100
                但由于我们同时也是脚本语言的设计者,完全可以通过预处理,将这种通过名称查询的语句解释成通过序号查询的功能调用,伪码如下:
                系统.窗口[1].控件[2].SetWidth(100)
                当然,由于用户可能在运行时增加脚本,因此,通过字符操作的属性处理也需要保留;
    6、关于属性编辑
        VCF的属性定义中,已将属性编辑的绝大部分内容定义了,但对属性的编辑,还可以参考Delphi的实现,提供自定义属性编辑器。
   

十一、总结
    1、VCF是我看到的对RTTI处理最完整的开源项目;
    2、其中对RTTI处理,有许多可取之处,特别是其对属性的封装,以及模板化的子类实现,都是非常好的思想;
    3、也有许多值得推敲的地方,如果要将它应用到我的项目中,满足我的要求,需要改进重写;
    4、VCF中除了RTTI外,还有其它部分的功能吸引我,准备在接下来的几天内,将那些内容也好好分析一下。
    
    补充一点,我昨天提的AGG,在VCF中是作为第三方产品,在底层实现2D图形的处理,看来,AGG还真是好东西呀。

posted on 2005-01-17 06:51 linkman的学习记录 阅读(1210) 评论(2)  编辑 收藏

Feedback

# re: VCF(Visual Component Framework)中的RTTI功能 2005-01-18 09:59 张志明
7.0啊,7.0.


# re: VCF(Visual Component Framework)中的RTTI功能 2008-08-28 10:39 lin.wei
LiaoChangBin,你从亚控跳到MCGS,真让我失望

标题  
姓名  
主页
验证码 *
内容   
  登录  使用高级评论  Top
[使用Ctrl+Enter键可以直接提交]