让C++的对象支持多类型

 借助 C/C++ 的union,可以设计出近似弱类型的变量,同一类型的变量可以承载不同类型的数据。比如说一个对象A,可以按如下不同的类型使用:

A a = 1;

A b = 1.1;

A c = "abc";

A d = true;

使用的时候可以按照其真实的类型来使用。比如字符串c以调用c.size()获得其长度。


这个想法来源于两个开源库的基础数据类型设计,一个是xmlrpclib库中XmlRpcValue设计,一个是xpdf中Object设计。非常

的巧妙。核心在于定义一个union承载实际的数据,定义一个enum来标识数据实际的类型。
XmlRpcValue的数据承载部分设计为:

  1.     union {
  2.       bool          asBool;
  3.       int           asInt;
  4.       double        asDouble;
  5.       struct tm*    asTime;
  6.       std::string*  asString;
  7.       BinaryData*   asBinary;
  8.       ValueArray*   asArray;
  9.       ValueStruct*  asStruct;
  10.     } _value;

支持的类型如下定义:

  1. enum Type {
  2.       TypeInvalid,
  3.       TypeBoolean,
  4.       TypeInt,
  5.       TypeDouble,
  6.       TypeString,
  7.       TypeDateTime,
  8.       TypeBase64,
  9.       TypeArray,
  10.       TypeStruct
  11.     };


在使用此类对象的时候,先设置该对象的类型,然后再根据实际的类型进行运算。其实质仍然是严格类型的。但是从使用者

的角度看来,却是弱类型的。
此类对象的使用会对效率和空间有一定的影响。但影响都不大。
时间方面的影响主要在于很多时候需要进行类型判定,若类型不匹配,则无法完成运算。值得注意的是,很多类型匹配可以

在编译期间完成。比如,XmpRpcValue a = 1; XmlRpcValue b = true;
空间方面主要是union分配空间是以最大的成员进行分配,但是如果大量使用指针,空间的多余耗费则不会很大。


 

xmlrpclib库中XmlRpcValue核心代码如下(已删除部分不相关代码)

  1.   class XmlRpcValue {
  2.   public:
  3.     enum Type {
  4.       TypeInvalid,
  5.       TypeBoolean,
  6.       TypeInt,
  7.       TypeDouble,
  8.       TypeString,
  9.       TypeDateTime,
  10.       TypeBase64,
  11.       TypeArray,
  12.       TypeStruct
  13.     };
  14.     // Non-primitive types
  15.     typedef std::vector<char> BinaryData;
  16.     typedef std::vector<XmlRpcValue> ValueArray;
  17.     typedef std::map<std::string, XmlRpcValue> ValueStruct;
  18.     //! Constructors
  19.     XmlRpcValue() : _type(TypeInvalid) { _value.asBinary = 0; }
  20.     XmlRpcValue(bool value) : _type(TypeBoolean) { _value.asBool = value; }
  21.     XmlRpcValue(int value)  : _type(TypeInt) { _value.asInt = value; }
  22.     XmlRpcValue(double value)  : _type(TypeDouble) { _value.asDouble = value; }
  23.     XmlRpcValue(std::string const& value) : _type(TypeString) 
  24.     { _value.asString = new std::string(value); }
  25.     XmlRpcValue(const char* value)  : _type(TypeString)
  26.     { _value.asString = new std::string(value); }
  27.     XmlRpcValue(struct tm* value)  : _type(TypeDateTime) 
  28.     { _value.asTime = new struct tm(*value); }
  29.     XmlRpcValue(void* value, int nBytes)  : _type(TypeBase64)
  30.     {
  31.       _value.asBinary = new BinaryData((char*)value, ((char*)value)+nBytes);
  32.     }
  33.     //! Construct from xml, beginning at *offset chars into the string, updates offset
  34.     XmlRpcValue(std::string const& xml, int* offset) : _type(TypeInvalid)
  35.     { if ( ! fromXml(xml,offset)) _type = TypeInvalid; }
  36.     //! Copy
  37.     XmlRpcValue(XmlRpcValue const& rhs) : _type(TypeInvalid) { *this = rhs; }
  38.     //! Destructor (make virtual if you want to subclass)
  39.     /*virtual*/ ~XmlRpcValue() { invalidate(); }
  40.     //! Erase the current value
  41.     void clear() { invalidate(); }
  42.     // Operators
  43.     XmlRpcValue& operator=(XmlRpcValue const& rhs);
  44.     XmlRpcValue& operator=(int const& rhs) { return operator=(XmlRpcValue(rhs)); }
  45.     XmlRpcValue& operator=(double const& rhs) { return operator=(XmlRpcValue(rhs)); }
  46.     XmlRpcValue& operator=(const char* rhs) { return operator=(XmlRpcValue(std::string(rhs))); }
  47.     bool operator==(XmlRpcValue const& other) const;
  48.     bool operator!=(XmlRpcValue const& other) const;
  49.     operator bool&()          { assertTypeOrInvalid(TypeBoolean); return _value.asBool; }
  50.     operator int&()           { assertTypeOrInvalid(TypeInt); return _value.asInt; }
  51.     operator double&()        { assertTypeOrInvalid(TypeDouble); return _value.asDouble; }
  52.     operator std::string&()   { assertTypeOrInvalid(TypeString); return *_value.asString; }
  53.     operator BinaryData&()    { assertTypeOrInvalid(TypeBase64); return *_value.asBinary; }
  54.     operator struct tm&()     { assertTypeOrInvalid(TypeDateTime); return *_value.asTime; }
  55.     XmlRpcValue const& operator[](int i) const { assertArray(i+1); return _value.asArray->at(i); }
  56.     XmlRpcValue& operator[](int i)             { assertArray(i+1); return _value.asArray->at(i); }
  57.     XmlRpcValue& operator[](std::string const& k) { assertStruct(); return (*_value.asStruct)[k]; }
  58.     XmlRpcValue& operator[](const char* k) { assertStruct(); std::string s(k); return (*_value.asStruct)[s]; 
  59. }
  60.     // Accessors
  61.     //! Return true if the value has been set to something.
  62.     bool valid() const { return _type != TypeInvalid; }
  63.     //! Return the type of the value stored. /see Type.
  64.     Type const &getType() const { return _type; }
  65.     //! Return the size for string, base64, array, and struct values.
  66.     int size() const;
  67.     //! Specify the size for array values. Array values will grow beyond this size if needed.
  68.     void setSize(int size)    { assertArray(size); }
  69.     //! Check for the existence of a struct member by name.
  70.     bool hasMember(const std::string& name) const;
  71.     // Type tag and values
  72.     Type _type;
  73.     // At some point I will split off Arrays and Structs into
  74.     // separate ref-counted objects for more efficient copying.
  75.     union {
  76.       bool          asBool;
  77.       int           asInt;
  78.       double        asDouble;
  79.       struct tm*    asTime;
  80.       std::string*  asString;
  81.       BinaryData*   asBinary;
  82.       ValueArray*   asArray;
  83.       ValueStruct*  asStruct;
  84.     } _value;
  85.     
  86.   };

 

xpdf中Object的核心代码如下

 

  1. enum ObjType {
  2.   // simple objects
  3.   objBool,          // boolean
  4.   objInt,           // integer
  5.   objReal,          // real
  6.   objString,            // string
  7.   objName,          // name
  8.   objNull,          // null
  9.   // complex objects
  10.   objArray,         // array
  11.   objDict,          // dictionary
  12.   objStream,            // stream
  13.   objRef,           // indirect reference
  14.   // special objects
  15.   objCmd,           // command name
  16.   objError,         // error return from Lexer
  17.   objEOF,           // end of file return from Lexer
  18.   objNone           // uninitialized object
  19. };
  20. class Object {
  21. public:
  22.   // Default constructor.
  23.   Object():
  24.     type(objNone) {}
  25.   // Initialize an object.
  26.   Object *initBool(GBool boolnA)
  27.     { initObj(objBool); booln = boolnA; return this; }
  28.   Object *initInt(int intgA)
  29.     { initObj(objInt); intg = intgA; return this; }
  30.   Object *initReal(double realA)
  31.     { initObj(objReal); real = realA; return this; }
  32.   Object *initString(GString *stringA)
  33.     { initObj(objString); string = stringA; return this; }
  34.   Object *initName(char *nameA)
  35.     { initObj(objName); name = copyString(nameA); return this; }
  36.   Object *initNull()
  37.     { initObj(objNull); return this; }
  38.   Object *initArray(XRef *xref);
  39.   Object *initDict(XRef *xref);
  40.   Object *initDict(Dict *dictA);
  41.   Object *initStream(Stream *streamA);
  42.   Object *initRef(int numA, int genA)
  43.     { initObj(objRef); ref.num = numA; ref.gen = genA; return this; }
  44.   Object *initCmd(char *cmdA)
  45.     { initObj(objCmd); cmd = copyString(cmdA); return this; }
  46.   Object *initError()
  47.     { initObj(objError); return this; }
  48.   Object *initEOF()
  49.     { initObj(objEOF); return this; }
  50.   // Copy an object.
  51.   Object *copy(Object *obj);
  52.   // If object is a Ref, fetch and return the referenced object.
  53.   // Otherwise, return a copy of the object.
  54.   Object *fetch(XRef *xref, Object *obj);
  55.   // Free object contents.
  56.   void free();
  57.   // Type checking.
  58.   ObjType getType() { return type; }
  59.   GBool isBool() { return type == objBool; }
  60.   GBool isInt() { return type == objInt; }
  61.   GBool isReal() { return type == objReal; }
  62.   GBool isNum() { return type == objInt || type == objReal; }
  63.   GBool isString() { return type == objString; }
  64.   GBool isName() { return type == objName; }
  65.   GBool isNull() { return type == objNull; }
  66.   GBool isArray() { return type == objArray; }
  67.   GBool isDict() { return type == objDict; }
  68.   GBool isStream() { return type == objStream; }
  69.   GBool isRef() { return type == objRef; }
  70.   GBool isCmd() { return type == objCmd; }
  71.   GBool isError() { return type == objError; }
  72.   GBool isEOF() { return type == objEOF; }
  73.   GBool isNone() { return type == objNone; }
  74.   // Special type checking.
  75.   GBool isName(char *nameA)
  76.     { return type == objName && !strcmp(name, nameA); }
  77.   GBool isDict(char *dictType);
  78.   GBool isStream(char *dictType);
  79.   GBool isCmd(char *cmdA)
  80.     { return type == objCmd && !strcmp(cmd, cmdA); }
  81.   // Accessors.  NB: these assume object is of correct type.
  82.   GBool getBool() { return booln; }
  83.   int getInt() { return intg; }
  84.   double getReal() { return real; }
  85.   double getNum() { return type == objInt ? (double)intg : real; }
  86.   GString *getString() { return string; }
  87.   char *getName() { return name; }
  88.   Array *getArray() { return array; }
  89.   Dict *getDict() { return dict; }
  90.   Stream *getStream() { return stream; }
  91.   Ref getRef() { return ref; }
  92.   int getRefNum() { return ref.num; }
  93.   int getRefGen() { return ref.gen; }
  94.   char *getCmd() { return cmd; }
  95.   // Array accessors.
  96.   int arrayGetLength();
  97.   void arrayAdd(Object *elem);
  98.   Object *arrayGet(int i, Object *obj);
  99.   Object *arrayGetNF(int i, Object *obj);
  100.   // Dict accessors.
  101.   int dictGetLength();
  102.   void dictAdd(char *key, Object *val);
  103.   GBool dictIs(char *dictType);
  104.   Object *dictLookup(char *key, Object *obj);
  105.   Object *dictLookupNF(char *key, Object *obj);
  106.   char *dictGetKey(int i);
  107.   Object *dictGetVal(int i, Object *obj);
  108.   Object *dictGetValNF(int i, Object *obj);
  109.   // Stream accessors.
  110.   GBool streamIs(char *dictType);
  111.   void streamReset();
  112.   void streamClose();
  113.   int streamGetChar();
  114.   int streamLookChar();
  115.   char *streamGetLine(char *buf, int size);
  116.   Guint streamGetPos();
  117.   void streamSetPos(Guint pos, int dir = 0);
  118.   Dict *streamGetDict();
  119. private:
  120.   ObjType type;         // object type
  121.   union {           // value for each type:
  122.     GBool booln;        //   boolean
  123.     int intg;           //   integer
  124.     double real;        //   real
  125.     GString *string;        //   string
  126.     char *name;         //   name
  127.     Array *array;       //   array
  128.     Dict *dict;         //   dictionary
  129.     Stream *stream;     //   stream
  130.     Ref ref;            //   indirect reference
  131.     char *cmd;          //   command
  132.   };
  133. };

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值