借助 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的数据承载部分设计为:
- union {
- bool asBool;
- int asInt;
- double asDouble;
- struct tm* asTime;
- std::string* asString;
- BinaryData* asBinary;
- ValueArray* asArray;
- ValueStruct* asStruct;
- } _value;
支持的类型如下定义:
- enum Type {
- TypeInvalid,
- TypeBoolean,
- TypeInt,
- TypeDouble,
- TypeString,
- TypeDateTime,
- TypeBase64,
- TypeArray,
- TypeStruct
- };
在使用此类对象的时候,先设置该对象的类型,然后再根据实际的类型进行运算。其实质仍然是严格类型的。但是从使用者
的角度看来,却是弱类型的。
此类对象的使用会对效率和空间有一定的影响。但影响都不大。
时间方面的影响主要在于很多时候需要进行类型判定,若类型不匹配,则无法完成运算。值得注意的是,很多类型匹配可以
在编译期间完成。比如,XmpRpcValue a = 1; XmlRpcValue b = true;
空间方面主要是union分配空间是以最大的成员进行分配,但是如果大量使用指针,空间的多余耗费则不会很大。
xmlrpclib库中XmlRpcValue核心代码如下(已删除部分不相关代码)
- class XmlRpcValue {
- public:
- enum Type {
- TypeInvalid,
- TypeBoolean,
- TypeInt,
- TypeDouble,
- TypeString,
- TypeDateTime,
- TypeBase64,
- TypeArray,
- TypeStruct
- };
-
- typedef std::vector<char> BinaryData;
- typedef std::vector ValueArray;
- typedef std::map ValueStruct;
-
- XmlRpcValue() : _type(TypeInvalid) { _value.asBinary = 0; }
- XmlRpcValue(bool value) : _type(TypeBoolean) { _value.asBool = value; }
- XmlRpcValue(int value) : _type(TypeInt) { _value.asInt = value; }
- XmlRpcValue(double value) : _type(TypeDouble) { _value.asDouble = value; }
- XmlRpcValue(std::string const& value) : _type(TypeString)
- { _value.asString = new std::string(value); }
- XmlRpcValue(const char* value) : _type(TypeString)
- { _value.asString = new std::string(value); }
- XmlRpcValue(struct tm* value) : _type(TypeDateTime)
- { _value.asTime = new struct tm(*value); }
- XmlRpcValue(void* value, int nBytes) : _type(TypeBase64)
- {
- _value.asBinary = new BinaryData((char*)value, ((char*)value)+nBytes);
- }
-
- XmlRpcValue(std::string const& xml, int* offset) : _type(TypeInvalid)
- { if ( ! fromXml(xml,offset)) _type = TypeInvalid; }
-
- XmlRpcValue(XmlRpcValue const& rhs) : _type(TypeInvalid) { *this = rhs; }
-
- ~XmlRpcValue() { invalidate(); }
-
- void clear() { invalidate(); }
-
- XmlRpcValue& operator=(XmlRpcValue const& rhs);
- XmlRpcValue& operator=(int const& rhs) { return operator=(XmlRpcValue(rhs)); }
- XmlRpcValue& operator=(double const& rhs) { return operator=(XmlRpcValue(rhs)); }
- XmlRpcValue& operator=(const char* rhs) { return operator=(XmlRpcValue(std::string(rhs))); }
- bool operator==(XmlRpcValue const& other) const;
- bool operator!=(XmlRpcValue const& other) const;
- operator bool&() { assertTypeOrInvalid(TypeBoolean); return _value.asBool; }
- operator int&() { assertTypeOrInvalid(TypeInt); return _value.asInt; }
- operator double&() { assertTypeOrInvalid(TypeDouble); return _value.asDouble; }
- operator std::string&() { assertTypeOrInvalid(TypeString); return *_value.asString; }
- operator BinaryData&() { assertTypeOrInvalid(TypeBase64); return *_value.asBinary; }
- operator struct tm&() { assertTypeOrInvalid(TypeDateTime); return *_value.asTime; }
- XmlRpcValue const& operator[](int i) const { assertArray(i+1); return _value.asArray->at(i); }
- XmlRpcValue& operator[](int i) { assertArray(i+1); return _value.asArray->at(i); }
- XmlRpcValue& operator[](std::string const& k) { assertStruct(); return (*_value.asStruct)[k]; }
- XmlRpcValue& operator[](const char* k) { assertStruct(); std::string s(k); return (*_value.asStruct)[s];
- }
-
-
- bool valid() const { return _type != TypeInvalid; }
-
- Type const &getType() const { return _type; }
-
- int size() const;
-
- void setSize(int size) { assertArray(size); }
-
- bool hasMember(const std::string& name) const;
-
- Type _type;
-
-
- union {
- bool asBool;
- int asInt;
- double asDouble;
- struct tm* asTime;
- std::string* asString;
- BinaryData* asBinary;
- ValueArray* asArray;
- ValueStruct* asStruct;
- } _value;
-
- };
xpdf中Object的核心代码如下
- enum ObjType {
-
- objBool,
- objInt,
- objReal,
- objString,
- objName,
- objNull,
-
- objArray,
- objDict,
- objStream,
- objRef,
-
- objCmd,
- objError,
- objEOF,
- objNone
- };
- class Object {
- public:
-
- Object():
- type(objNone) {}
-
- Object *initBool(GBool boolnA)
- { initObj(objBool); booln = boolnA; return this; }
- Object *initInt(int intgA)
- { initObj(objInt); intg = intgA; return this; }
- Object *initReal(double realA)
- { initObj(objReal); real = realA; return this; }
- Object *initString(GString *stringA)
- { initObj(objString); string = stringA; return this; }
- Object *initName(char *nameA)
- { initObj(objName); name = copyString(nameA); return this; }
- Object *initNull()
- { initObj(objNull); return this; }
- Object *initArray(XRef *xref);
- Object *initDict(XRef *xref);
- Object *initDict(Dict *dictA);
- Object *initStream(Stream *streamA);
- Object *initRef(int numA, int genA)
- { initObj(objRef); ref.num = numA; ref.gen = genA; return this; }
- Object *initCmd(char *cmdA)
- { initObj(objCmd); cmd = copyString(cmdA); return this; }
- Object *initError()
- { initObj(objError); return this; }
- Object *initEOF()
- { initObj(objEOF); return this; }
-
- Object *copy(Object *obj);
-
-
- Object *fetch(XRef *xref, Object *obj);
-
- void free();
-
- ObjType getType() { return type; }
- GBool isBool() { return type == objBool; }
- GBool isInt() { return type == objInt; }
- GBool isReal() { return type == objReal; }
- GBool isNum() { return type == objInt || type == objReal; }
- GBool isString() { return type == objString; }
- GBool isName() { return type == objName; }
- GBool isNull() { return type == objNull; }
- GBool isArray() { return type == objArray; }
- GBool isDict() { return type == objDict; }
- GBool isStream() { return type == objStream; }
- GBool isRef() { return type == objRef; }
- GBool isCmd() { return type == objCmd; }
- GBool isError() { return type == objError; }
- GBool isEOF() { return type == objEOF; }
- GBool isNone() { return type == objNone; }
-
- GBool isName(char *nameA)
- { return type == objName && !strcmp(name, nameA); }
- GBool isDict(char *dictType);
- GBool isStream(char *dictType);
- GBool isCmd(char *cmdA)
- { return type == objCmd && !strcmp(cmd, cmdA); }
-
- GBool getBool() { return booln; }
- int getInt() { return intg; }
- double getReal() { return real; }
- double getNum() { return type == objInt ? (double)intg : real; }
- GString *getString() { return string; }
- char *getName() { return name; }
- Array *getArray() { return array; }
- Dict *getDict() { return dict; }
- Stream *getStream() { return stream; }
- Ref getRef() { return ref; }
- int getRefNum() { return ref.num; }
- int getRefGen() { return ref.gen; }
- char *getCmd() { return cmd; }
-
- int arrayGetLength();
- void arrayAdd(Object *elem);
- Object *arrayGet(int i, Object *obj);
- Object *arrayGetNF(int i, Object *obj);
-
- int dictGetLength();
- void dictAdd(char *key, Object *val);
- GBool dictIs(char *dictType);
- Object *dictLookup(char *key, Object *obj);
- Object *dictLookupNF(char *key, Object *obj);
- char *dictGetKey(int i);
- Object *dictGetVal(int i, Object *obj);
- Object *dictGetValNF(int i, Object *obj);
-
- GBool streamIs(char *dictType);
- void streamReset();
- void streamClose();
- int streamGetChar();
- int streamLookChar();
- char *streamGetLine(char *buf, int size);
- Guint streamGetPos();
- void streamSetPos(Guint pos, int dir = 0);
- Dict *streamGetDict();
- private:
- ObjType type;
- union {
- GBool booln;
- int intg;
- double real;
- GString *string;
- char *name;
- Array *array;
- Dict *dict;
- Stream *stream;
- Ref ref;
- char *cmd;
- };
- };