From ff17b44a411f7892600159aa769229716019c751 Mon Sep 17 00:00:00 2001 From: timoore Date: Wed, 15 Jul 2009 23:07:53 +0000 Subject: [PATCH] Extend properties to support new property types. An SGRawBase class has been added as a base class to the SGRawValue hierarchy so that SGPropertyValue functions don't necessarily need to know the type of the value stored in the node. A new SGRawValueContainer class stores properties that shouldn't be stored in the node itself. PropertyTraits indicates if a type is stored in the property node or externally. Add getValue and SetValue template member functions to SGPropertyNode. Read and write new extended properties. Rearrange props.hxx a bit so that the template magic actually works. Split out extended raw value virtual functions into a seperate base class. SGRawExtended is chosen as a base class of SGRawValue for extended property types. --- simgear/props/props.cxx | 208 ++++++++++++---------- simgear/props/props.hxx | 381 ++++++++++++++++++++++++++++++++-------- 2 files changed, 418 insertions(+), 171 deletions(-) diff --git a/simgear/props/props.cxx b/simgear/props/props.cxx index 0c783eb4..042275e4 100644 --- a/simgear/props/props.cxx +++ b/simgear/props/props.cxx @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -385,7 +386,7 @@ inline const char * SGPropertyNode::get_string () const { if (_tied) - return static_cast*>(_value.val)->getValue(); + return static_cast*>(_value.val)->getValue(); else return _local_val.string_val; } @@ -479,7 +480,7 @@ inline bool SGPropertyNode::set_string (const char * val) { if (_tied) { - if (static_cast*>(_value.val)->setValue(val)) { + if (static_cast*>(_value.val)->setValue(val)) { fireValueChanged(); return true; } else { @@ -538,53 +539,43 @@ SGPropertyNode::clearValue () const char * SGPropertyNode::make_string () const { - if (!getAttribute(READ)) - return ""; - - switch (_type) { - case ALIAS: - return _value.alias->getStringValue(); - case BOOL: - if (get_bool()) - return "true"; - else - return "false"; - case INT: - { - stringstream sstr; - sstr << get_int(); - _buffer = sstr.str(); - return _buffer.c_str(); - } - case LONG: - { - stringstream sstr; - sstr << get_long(); - _buffer = sstr.str(); - return _buffer.c_str(); - } - case FLOAT: - { - stringstream sstr; - sstr << get_float(); - _buffer = sstr.str(); - return _buffer.c_str(); + if (!getAttribute(READ)) + return ""; + switch (_type) { + case ALIAS: + return _value.alias->getStringValue(); + case BOOL: + return get_bool() ? "true" : "false"; + case STRING: + case UNSPECIFIED: + return get_string(); + case NONE: + return ""; + default: + break; } - case DOUBLE: - { - stringstream sstr; - sstr.precision( 10 ); - sstr << get_double(); - _buffer = sstr.str(); - return _buffer.c_str(); + stringstream sstr; + switch (_type) { + case INT: + sstr << get_int(); + break; + case LONG: + sstr << get_long(); + break; + case FLOAT: + sstr << get_float(); + break; + case DOUBLE: + sstr << std::setprecision(10) << get_double(); + break; + case EXTENDED: + static_cast(_value.val)->printOn(sstr); + break; + default: + return ""; } - case STRING: - case UNSPECIFIED: - return get_string(); - case NONE: - default: - return ""; - } + _buffer = sstr.str(); + return _buffer.c_str(); } /** @@ -660,68 +651,39 @@ SGPropertyNode::SGPropertyNode (const SGPropertyNode &node) { _local_val.string_val = 0; _value.val = 0; - switch (_type) { - case NONE: - break; - case ALIAS: + if (_type == NONE) + return; + if (_type == ALIAS) { _value.alias = node._value.alias; get(_value.alias); _tied = false; - break; + return; + } + if (_tied || _type == EXTENDED) { + _value.val = node._value.val->clone(); + return; + } + switch (_type) { case BOOL: - if (_tied) { - _tied = true; - _value.val = static_cast*>(node._value.val)->clone(); - } else { - _tied = false; - set_bool(node.get_bool()); - } + set_bool(node.get_bool()); break; case INT: - if (_tied) { - _tied = true; - _value.val = static_cast*>(node._value.val)->clone(); - } else { - _tied = false; - set_int(node.get_int()); - } + set_int(node.get_int()); break; case LONG: - if (_tied) { - _tied = true; - _value.val = static_cast*>(node._value.val)->clone(); - } else { - _tied = false; - set_long(node.get_long()); - } + set_long(node.get_long()); break; case FLOAT: - if (_tied) { - _tied = true; - _value.val = static_cast*>(node._value.val)->clone(); - } else { - _tied = false; - set_float(node.get_float()); - } + set_float(node.get_float()); break; case DOUBLE: - if (_tied) { - _tied = true; - _value.val = static_cast*>(node._value.val)->clone(); - } else { - _tied = false; - set_double(node.get_double()); - } + set_double(node.get_double()); break; case STRING: case UNSPECIFIED: - if (_tied) { - _tied = true; - _value.val = static_cast*>(node._value.val)->clone(); - } else { - _tied = false; - set_string(node.get_string()); - } + set_string(node.get_string()); + break; + default: break; } } @@ -1053,6 +1015,8 @@ SGPropertyNode::getType () const { if (_type == ALIAS) return _value.alias->getType(); + else if (_type == EXTENDED) + return _value.val->getType(); else return _type; } @@ -1528,6 +1492,12 @@ SGPropertyNode::setStringValue (const char * value) case UNSPECIFIED: result = set_string(value); break; + case EXTENDED: + { + stringstream sstr(value); + static_cast(_value.val)->readFrom(sstr); + } + break; case NONE: default: break; @@ -1547,8 +1517,10 @@ SGPropertyNode::setUnspecifiedValue (const char * value) clearValue(); _type = UNSPECIFIED; } - - switch (_type) { + Type type = _type; + if (type == EXTENDED) + type = _value.val->getType(); + switch (type) { case ALIAS: result = _value.alias->setUnspecifiedValue(value); break; @@ -1582,6 +1554,41 @@ SGPropertyNode::setUnspecifiedValue (const char * value) return result; } +std::ostream& SGPropertyNode::printOn(std::ostream& stream) const +{ + if (!getAttribute(READ)) + return stream; + switch (_type) { + case ALIAS: + return _value.alias->printOn(stream); + case BOOL: + stream << (get_bool() ? "true" : "false"); + break; + case INT: + stream << get_int(); + break; + case LONG: + stream << get_long(); + break; + case FLOAT: + stream << get_float(); + break; + case DOUBLE: + stream << get_double(); + break; + case STRING: + case UNSPECIFIED: + stream << get_string(); + break; + case EXTENDED: + static_cast(_value.val)->printOn(stream); + break; + case NONE: + break; + } + return stream; +} + template<> bool SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) @@ -1653,6 +1660,15 @@ SGPropertyNode::untie () _local_val.string_val = copy_string(val.c_str()); break; } + case EXTENDED: { + SGRawExtended* val = static_cast(_value.val); + _value.val = 0; // Prevent clearValue() from deleting + clearValue(); + _type = EXTENDED; + _value.val = val->makeContainer(); + delete val; + break; + } case NONE: default: break; diff --git a/simgear/props/props.hxx b/simgear/props/props.hxx index adcfdc47..960d2aea 100644 --- a/simgear/props/props.hxx +++ b/simgear/props/props.hxx @@ -18,6 +18,10 @@ #include #include +#include +#include + +#include #if PROPS_STANDALONE #else @@ -35,6 +39,30 @@ namespace simgear { +template +std::istream& readFrom(std::istream& stream, T& result) +{ + stream >> result; + return stream; +} + +/** + * Parse a string as an object of a given type. + * XXX no error behavior yet. + * + * @tparam T the return type + * @param str the string + * @return the object. + */ +template +inline T parseString(const std::string& str) +{ + std::istringstream stream(str); + T result; + readFrom(stream, result); + return result; +} + /** * Property value types. */ @@ -86,60 +114,45 @@ namespace simgear namespace props { +/** + * The possible types of an SGPropertyNode. Types that appear after + * EXTENDED are not stored in the SGPropertyNode itself. + */ enum Type { - NONE = 0, - ALIAS, + NONE = 0, /**< The node hasn't been assigned a value yet. */ + ALIAS, /**< The node "points" to another node. */ BOOL, INT, LONG, FLOAT, DOUBLE, STRING, - UNSPECIFIED + UNSPECIFIED, + EXTENDED /**< The node's value is not stored in the property; + * the actual value and type is retrieved from an + * SGRawValue node. This type is never returned by @see + * SGPropertyNode::getType. + */ }; +template struct PropertyTraits; -template -struct PropertyTraits -{ - static const Type type_tag = NONE; -}; - -template<> -struct PropertyTraits -{ - static const Type type_tag = BOOL; -}; - -template<> -struct PropertyTraits -{ - static const Type type_tag = INT; -}; - -template<> -struct PropertyTraits -{ - static const Type type_tag = LONG; -}; - -template<> -struct PropertyTraits -{ - static const Type type_tag = FLOAT; -}; +#define DEFINTERNALPROP(TYPE, PROP) \ +template<> \ +struct PropertyTraits \ +{ \ + static const Type type_tag = PROP; \ + enum { Internal = 1 }; \ +} -template<> -struct PropertyTraits -{ - static const Type type_tag = DOUBLE; -}; +DEFINTERNALPROP(bool, BOOL); +DEFINTERNALPROP(int, INT); +DEFINTERNALPROP(long, LONG); +DEFINTERNALPROP(float, FLOAT); +DEFINTERNALPROP(double, DOUBLE); +DEFINTERNALPROP(const char *, STRING); +#undef DEFINTERNALPROP -template<> -struct PropertyTraits -{ - static const Type type_tag = STRING; -}; } } @@ -158,12 +171,73 @@ struct PropertyTraits // a small performance hit for that. //////////////////////////////////////////////////////////////////////// -/** Base for virtual destructor +/** + * Base class for SGRawValue classes that holds no type + * information. This allows some generic manipulation of the + * SGRawValue object. */ -class SGRawBase +class SGRaw +{ +public: + /** + * Get the type enumeration for the raw value. + * + * @return the type. + */ + virtual simgear::props::Type getType() const = 0; + virtual ~SGRaw() {} + + /** + * Create a new deep copy of this raw value. + * + * The copy will contain its own version of the underlying value + * as well, and will be the same type. + * + * @return A deep copy of the current object. + */ + virtual SGRaw* clone() const = 0; + +}; + +class SGRawExtended : public SGRaw { public: - virtual ~SGRawBase() {} + /** + * Make an SGRawValueContainer from the SGRawValue. + * + * This is a virtual function of SGRawExtended so that + * SGPropertyNode::untie doesn't need to know the type of an + * extended property. + */ + virtual SGRawExtended* makeContainer() const = 0; + /** + * Write value out to a stream + */ + virtual std::ostream& printOn(std::ostream& stream) const = 0; + /** + * Read value from a stream and store it. + */ + virtual std::istream& readFrom(std::istream& stream) = 0; +}; + +// Choose between different base classes based on whether the value is +// stored internal to the property node. This frees us from defining +// the virtual functions in the SGRawExtended interface where they +// don't make sense, e.g. readFrom for the const char* type. +template::Internal> +class SGRawBase; + +template +class SGRawBase : public SGRaw +{ +}; + +template +class SGRawBase : public SGRawExtended +{ + virtual SGRawExtended* makeContainer() const; + virtual std::ostream& printOn(std::ostream& stream) const; + virtual std::istream& readFrom(std::istream& stream); }; /** @@ -183,10 +257,7 @@ public: * set, and clone the underlying value. The SGRawValue may change * frequently during a session as a value is retyped or bound and * unbound to various data source, but the abstract SGPropertyNode - * layer insulates the application from those changes. The raw value - * contains no facilities for data binding or for type conversion: it - * is simply the abstraction of a primitive data type (or a compound - * data type, in the case of a string).

+ * layer insulates the application from those changes. * *

The SGPropertyNode class always keeps a *copy* of a raw value, * not the original one passed to it; if you override a derived class @@ -202,9 +273,10 @@ public: * @see SGRawValueFunctionsIndexed * @see SGRawValueMethods * @see SGRawValueMethodsIndexed + * @see SGRawValueContainer */ template -class SGRawValue : public SGRawBase +class SGRawValue : public SGRawBase { public: @@ -259,16 +331,16 @@ public: /** - * Create a new deep copy of this raw value. - * - * The copy will contain its own version of the underlying value - * as well. - * - * @return A deep copy of the current object. + * Return the type tag for this raw value type. */ - virtual SGRawValue * clone () const = 0; + virtual simgear::props::Type getType() const + { + return simgear::props::PropertyTraits::type_tag; + } }; + + //////////////////////////////////////////////////////////////////////// // Default values for every type. //////////////////////////////////////////////////////////////////////// @@ -332,8 +404,8 @@ public: * * The copy will use the same external pointer as the original. */ - virtual SGRawValue * clone () const { - return new SGRawValuePointer(_ptr); + virtual SGRaw* clone () const { + return new SGRawValuePointer(_ptr); } private: @@ -407,8 +479,8 @@ public: /** * Create a copy of this raw value, bound to the same functions. */ - virtual SGRawValue * clone () const { - return new SGRawValueFunctions(_getter,_setter); + virtual SGRaw* clone () const { + return new SGRawValueFunctions(_getter,_setter); } private: @@ -444,8 +516,8 @@ public: if (_setter) { (*_setter)(_index, value); return true; } else return false; } - virtual SGRawValue * clone () const { - return new SGRawValueFunctionsIndexed(_index, _getter, _setter); + virtual SGRaw* clone () const { + return new SGRawValueFunctionsIndexed(_index, _getter, _setter); } private: int _index; @@ -477,8 +549,8 @@ public: if (_setter) { (_obj.*_setter)(value); return true; } else return false; } - virtual SGRawValue * clone () const { - return new SGRawValueMethods(_obj, _getter, _setter); + virtual SGRaw* clone () const { + return new SGRawValueMethods(_obj, _getter, _setter); } private: C &_obj; @@ -511,8 +583,8 @@ public: if (_setter) { (_obj.*_setter)(_index, value); return true; } else return false; } - virtual SGRawValue * clone () const { - return new SGRawValueMethodsIndexed(_obj, _index, _getter, _setter); + virtual SGRaw* clone () const { + return new SGRawValueMethodsIndexed(_obj, _index, _getter, _setter); } private: C &_obj; @@ -521,6 +593,71 @@ private: setter_t _setter; }; +/** + * A raw value that contains its value. This provides a way for + * property nodes to contain values that shouldn't be stored in the + * property node itself. + */ +template +class SGRawValueContainer : public SGRawValue +{ +public: + + /** + * Explicit constructor. + */ + SGRawValueContainer(const T& obj) : _obj(obj) {} + + /** + * Destructor. + */ + virtual ~SGRawValueContainer() {} + + /** + * Get the underlying value. + */ + virtual T getValue() const { return _obj; } + + /** + * Set the underlying value. + * + * This method will dereference the pointer and change the + * variable's value. + */ + virtual bool setValue (T value) { _obj = value; return true; } + + /** + * Create a copy of this raw value. + */ + virtual SGRaw* clone () const { + return new SGRawValueContainer(_obj); + } + +private: + T _obj; +}; + +template +SGRawExtended* SGRawBase::makeContainer() const +{ + return new SGRawValueContainer(static_cast*>(this) + ->getValue()); +} + +template +std::ostream& SGRawBase::printOn(std::ostream& stream) const +{ + return stream << static_cast*>(this)->getValue(); +} + +template +std::istream& SGRawBase::readFrom(std::istream& stream) +{ + T value; + simgear::readFrom(stream, value); + static_cast*>(this)->setValue(value); + return stream; +} /** * The smart pointer that manage reference counting @@ -981,7 +1118,17 @@ public: */ const char * getStringValue () const; - + /** + * Get a value from a node. If the actual type of the node doesn't + * match the desired type, a conversion isn't guaranteed. + */ + template + T getValue(typename boost::enable_if_c::Internal> + ::type* dummy = 0) const; + // Getter for extended property + template + T getValue(typename boost::disable_if_c::Internal> + ::type* dummy = 0) const; /** * Set a bool value for this node. @@ -1030,7 +1177,21 @@ public: */ bool setUnspecifiedValue (const char * value); + template + bool setValue(const T& val, + typename boost::enable_if_c::Internal> + ::type* dummy = 0); + template + bool setValue(const T& val, + typename boost::disable_if_c::Internal> + ::type* dummy = 0); + + /** + * Print the value of the property to a stream. + */ + std::ostream& printOn(std::ostream& stream) const; + // // Data binding. // @@ -1428,7 +1589,6 @@ private: */ const char * make_string () const; - /** * Trace a read access. */ @@ -1468,7 +1628,7 @@ private: // The right kind of pointer... union { SGPropertyNode * alias; - SGRawBase* val; + SGRaw* val; } _value; union { @@ -1614,7 +1774,8 @@ inline bool setValue (SGPropertyNode* node, const std::string& value) template bool SGPropertyNode::tie(const SGRawValue &rawValue, bool useDefault) { - if (_type == simgear::props::ALIAS || _tied) + using namespace simgear::props; + if (_type == ALIAS || _tied) return false; useDefault = useDefault && hasValue(); @@ -1622,13 +1783,14 @@ bool SGPropertyNode::tie(const SGRawValue &rawValue, bool useDefault) if (useDefault) old_val = getValue(this); clearValue(); - _type = simgear::props::PropertyTraits::type_tag; + if (PropertyTraits::Internal) + _type = PropertyTraits::type_tag; + else + _type = EXTENDED; _tied = true; _value.val = rawValue.clone(); - if (useDefault) - setValue(this, old_val); - + setValue(old_val); return true; } @@ -1636,6 +1798,75 @@ template<> bool SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault); +template +T SGPropertyNode::getValue(typename boost::disable_if_c::Internal>::type* dummy) const +{ + using namespace simgear::props; + if (_attr == (READ|WRITE) && _type == EXTENDED + && _value.val->getType() == PropertyTraits::type_tag) { + return static_cast*>(_value.val)->getValue(); + } + if (getAttribute(TRACE_READ)) + trace_read(); + if (!getAttribute(READ)) + return SGRawValue::DefaultValue; + switch (_type) { + case EXTENDED: + if (_value.val->getType() == PropertyTraits::type_tag) + return static_cast*>(_value.val)->getValue(); + break; + case STRING: + case UNSPECIFIED: + return simgear::parseString(make_string()); + break; + } + return SGRawValue::DefaultValue; +} + +template +inline T SGPropertyNode::getValue(typename boost::enable_if_c::Internal>::type* dummy) const +{ + return ::getValue(this); +} + +template +bool SGPropertyNode::setValue(const T& val, + typename boost::disable_if_c::Internal>::type* dummy) +{ + using namespace simgear::props; + if (_attr == (READ|WRITE) && _type == EXTENDED + && _value.val->getType() == PropertyTraits::type_tag) { + static_cast*>(_value.val)->setValue(val); + return true; + } + if (getAttribute(WRITE) + && ((_type == EXTENDED + && _value.val->getType() == PropertyTraits::type_tag) + || _type == NONE || _type == UNSPECIFIED)) { + if (_type == NONE || _type == UNSPECIFIED) { + clearValue(); + _type = EXTENDED; + _value.val = new SGRawValueContainer(val); + } else { + static_cast*>(_value.val)->setValue(val); + } + if (getAttribute(TRACE_WRITE)) + trace_write(); + return true; + } + return false; +} + +template +inline bool SGPropertyNode::setValue(const T& val, + typename boost::enable_if_c::Internal>::type* dummy) +{ + return ::setValue(this, val); +} #endif // __PROPS_HXX // end of props.hxx -- 2.39.5