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