#include #include #include #include // This class implements a generic property set. It lets you // associate any object/type with a name, and retreive it by that name // in a type safe manner. class PropertySet { public: // Constructor. If noPropertyReplacement is specified, then once a // property exists for a name in the set, it cannot be replaced. // If noPropertyRetyping is set and we are allowed to replace // properties, we cannot do so with a object of type different from // that existing property. PropertySet(bool noPropertyReplacement = false, bool noPropertyRetyping = false) : m_noPropertyReplacement(noPropertyReplacement), m_noPropertyRetyping(noPropertyRetyping) { } ~PropertySet() { for(SetType::iterator iter = theSet.begin(); iter != theSet.end(); ++iter) delete (iter->second); } // This setter adds a copy of the value to the property set template void Set(const string& name, const ValType& value) { SetType::iterator result = theSet.find(name); if (result == theSet.end()) { theSet.insert(SetType::value_type(name, new Value(value))); } else { BaseValue* bt = result->second; Value* vt = dynamic_cast< Value* >(bt); if (vt) { if (m_noPropertyReplacement) { string msg = "Cannot replace property \""; msg += name; msg += "\" in this property set"; throw msg; } else { delete result->second; result->second = new Value(value); } } else { if (m_noPropertyRetyping) { string msg = "Retyping disallowed: tried to change the type " "of property \""; msg += name; msg += "\" from type \""; msg += bt->typeName; msg += "\" to type \""; msg += typeid(value).name(); msg += "\""; throw msg; } else { theSet.erase(name); theSet.insert(SetType::value_type(name, new Value(value))); } } } } // Retrieves the pointer to the property set item pointed to by // name. If no such entry exusts, false is returned, and value is // set to NULL. Otherwise true is returned, and value is set to // point to that entry. template bool Get(const string& name, ValType& value) { bool valExists = false; SetType::iterator result = theSet.find(name); if (result != theSet.end()) { BaseValue* bt = result->second; Value* vt = dynamic_cast< Value* >(bt); if (!vt) { string msg = "Type mismatch: trying to get type \""; msg += bt->typeName; msg += "\" as type \""; msg += typeid(value).name(); msg += "\" while retreiving property set item \""; msg += name; msg += "\""; throw msg; } value = vt->m_val; valExists = true; } return valExists; } bool Remove(const string& name) { return theSet.erase(name); } private: struct BaseValue { virtual ~BaseValue() {} const char* typeName; }; template struct Value : public BaseValue { Value(const HeldType& val) : m_val(val) { typeName = typeid(val).name(); } HeldType m_val; }; // The map has to contain BaseValue pointers because when we stick // a Value into the map as a BaseValue, it gets copy constructed // and loses all the Value specific information, but if its a // pointer, then just the pointer value gets copied, so we are ok. typedef map SetType; SetType theSet; bool m_noPropertyReplacement; bool m_noPropertyRetyping; }; int main() { PropertySet ps; ps.Set("one", 1); }