linkman

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

导航

关于VCF(Visual Component Framework)(2)

    今天分析完了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-14 21:33  linkman  阅读(2235)  评论(0编辑  收藏  举报