From f1dd7901cbb9ed5fb342ae1ec2ac982b915fb9f0 Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Thu, 21 Jan 2021 12:53:34 +0000 Subject: [PATCH] simgear/props/props.*: added locking using read/write mutex. Added a std::shared_mutex to SGPropertyNode and changed all props code to use shared or exclusive locks as necessary whenever reading/writing data. SG_PROPERTY_LOCKING=0 or 1 can be used to control whether locking is active. Default is 1. SG_PROPERTY_LOCKING_VERBOSE=0 or 1 can be used to cause diagnostics to be generated if we get lock contention. Default is 0. Removed most of the inline implementation code in props.hxx - now that we use locking, nearly all calls eventually end up in props.cxx in order to take out a lock so there's no point inlining. For template code this also means that we now explicitly instantiate / specialise in props.cxx. Reformateed props.hxx to use consistent indentation. Also condensed identical comments for related methods such as getBoolValue(), getIntValue() etc, which results in more readable visual grouping. Much of the internal implementation code for SGPropertyNode is now in the form of static methods in a new SGPropertyNodeImpl class in props.cxx. This class is marked as a friend of SGPropertyNode in props.hxx so these methods can access all SGPropertyNode internal data without being declared in props.hxx. So one can change implementation code without recompiling everything. Removed TEST_READ and TEST_WRITE macros. Removed SGPropertyNode::MAX_STRING_LEN - was only used in compare_strings() and it is unnecessary. --- simgear/props/props.cxx | 3575 ++++++++++++++++++++++++++------------- simgear/props/props.hxx | 2379 ++++++++++---------------- 2 files changed, 3330 insertions(+), 2624 deletions(-) diff --git a/simgear/props/props.cxx b/simgear/props/props.cxx index 72095259..42969ddd 100755 --- a/simgear/props/props.cxx +++ b/simgear/props/props.cxx @@ -51,11 +51,236 @@ using std::stringstream; using namespace simgear; +/* +Locking support. + +All code that reads/writes SGPropertyNode data has to create either a +SGPropertyLockShared or a SGPropertyLockExclusive. + +To avoid deadlocks, code must never lock a node when holding lock on a +child/grandchild/... node. +*/ + +static bool s_property_locking_first_time = true; +static bool s_property_locking_active = true; +static bool s_property_locking_verbose = false; + +/* Abstract lock for shared/exclusive locks. We need this for code that doesn't +care whether a lock is shared or exclusive, so it can be called by code that +has exclusive or shared locks. */ +struct SGPropertyLock +{ + /* Returns true/false if environmental variable is 1 or 0, otherwise + . */ + static bool env_default(const char* name, bool default_) + { + bool ret; + const char* value = getenv(name); + if (!value) ret = default_; + else if (!strcmp(value, "0")) ret = false; + else if (!strcmp(value, "1")) ret = true; + else { + std::cerr << __FILE__ << ":" << __LINE__ << ":" + << " unrecognised " << name + << " should be 0 or 1: " << value + << "\n"; + ret = default_; + } + std::cerr << __FILE__ << ":" << __LINE__ << ":" + << " name=" << name + << " value=" << (value ? value : "") + << " returning: " << ret + << "\n"; + return ret; + } + + static void init_static() + { + if (s_property_locking_first_time) { + s_property_locking_first_time = false; + s_property_locking_active = env_default("SG_PROPERTY_LOCKING", true); + s_property_locking_verbose = env_default("SG_PROPERTY_LOCKING_VERBOSE", false); + } + } + + SGPropertyLock() + { + init_static(); + } + + /* These are provided for the rare situations where share/non-shared + agnostic code needs to release/acquire a lock. */ + virtual void acquire() = 0; + virtual void release() = 0; + + /* Acquires shared or exclusive lock on , generating various + diagnostics depending on s_property_locking_verbose. */ + void acquire_internal(const SGPropertyNode& node, bool shared) + { + if (!s_property_locking_active) { + return; + } + + if (!s_property_locking_verbose) { + if (shared) node._mutex.lock_shared(); + else node._mutex.lock(); + return; + } + + /* Verbose. Try non-blocking lock first. */ + try { + bool ok = (shared) ? node._mutex.try_lock_shared() : node._mutex.try_lock(); + if (ok) return; + } + catch (std::exception& e) { + std::cerr << __FILE__ << ":" << __LINE__ << ":" + << (shared ? " shared" : " exclusive") << " try-lock failed." + << " &node=" << &node + << " _name=" << node._name + << ": " << e.what() + << "\n"; + throw; + } + + /* Non-blocking call failed to acquire, so now do a blocking lock. */ + std::cerr << __FILE__ << ":" << __LINE__ << ":" + << (shared ? " shared" : " exclusive") << " lock contention" + << " &node=" << &node + << " _name=" << node._name + << "\n"; + try { + if (shared) node._mutex.lock_shared(); + else node._mutex.lock(); + } + catch (std::exception& e) { + std::cerr << __FILE__ << ":" << __LINE__ << ":" + << (shared ? " shared" : " exclusive") << " lock failed:" + << " &node=" << &node + << " _name=" << node._name + << ": " << e.what() + << "\n"; + throw; + } + } + + void release_internal(const SGPropertyNode& node, bool shared) + { + if (!s_property_locking_active) { + return; + } + if (shared) node._mutex.unlock_shared(); + else node._mutex.unlock(); + } +}; + +/* Scoped exclusive lock of a SGPropertyNode. */ +struct SGPropertyLockExclusive : SGPropertyLock +{ + SGPropertyLockExclusive() + : + SGPropertyLock() + { + } + + SGPropertyLockExclusive(const SGPropertyNode& node) + : + SGPropertyLock() + { + assign(node); + } + + void assign(const SGPropertyNode& node) + { + assert(!m_node); + m_node = &node; + acquire(); + } + + void acquire() override + { + assert(m_node); + assert(!m_own); + acquire_internal(*m_node, false /*shared*/); + m_own = true; + } + void release() override + { + assert(m_own); + release_internal(*m_node, false /*shared*/); + m_own = false; + } + + ~SGPropertyLockExclusive() + { + if (m_own) release(); + } + + const SGPropertyNode* m_node = nullptr; + bool m_own = false; + + private: + SGPropertyLockExclusive(const SGPropertyLockExclusive&); +}; + +/* Scoped shared lock of a SGPropertyNode. */ +struct SGPropertyLockShared : SGPropertyLock +{ + SGPropertyLockShared() + : + SGPropertyLock(), + m_node(nullptr) + { + } + + /* Takes out shared lock for . */ + SGPropertyLockShared(const SGPropertyNode& node) + : + SGPropertyLock() + { + assign(node); + } + + /* Sets our SGPropertyNode and takes out a shared lock. Can only + be called if we don't have a SGPropertyNode. */ + void assign(const SGPropertyNode& node) + { + assert(!m_node); + m_node = &node; + acquire(); + } + + void acquire() override + { + assert(m_node); + assert(!m_own); + acquire_internal(*m_node, true /*shared*/); + m_own = true; + } + void release() override + { + assert(m_own); + release_internal(*m_node, true /*shared*/); + m_own = false; + } + + ~SGPropertyLockShared() + { + if (m_own) release(); + } + + const SGPropertyNode* m_node = nullptr; + bool m_own = false; + + private: + SGPropertyLockShared(const SGPropertyLockShared&); +}; + + struct SGPropertyNodeListeners { /* Protect _num_iterators and _items. We use a recursive mutex to allow nested access to work as normal. */ - std::recursive_mutex _rmutex; + //std::recursive_mutex _rmutex; /* This keeps a count of the current number of nested invocations of forEachListener(). If non-zero, other code higher up the stack is iterating @@ -82,13 +307,6 @@ public: }; -//////////////////////////////////////////////////////////////////////// -// Convenience macros for value access. -//////////////////////////////////////////////////////////////////////// - -#define TEST_READ(dflt) if (!getAttribute(READ)) return dflt -#define TEST_WRITE if (!getAttribute(WRITE)) return false - //////////////////////////////////////////////////////////////////////// // Local path normalization code. //////////////////////////////////////////////////////////////////////// @@ -365,23 +583,24 @@ copy_string (const char * s) } static bool -compare_strings (const char * s1, const char * s2) +strings_equal (const char * s1, const char * s2) { - return !strncmp(s1, s2, SGPropertyNode::MAX_STRING_LEN); + return !strcmp(s1, s2); } + /** * Locate a child node by name and index. */ template static int -find_child (Itr begin, Itr end, int index, const PropertyList& nodes) +find_child(SGPropertyLock& lock, Itr begin, Itr end, int index, const PropertyList& nodes) { size_t nNodes = nodes.size(); #if PROPS_STANDALONE for (int i = 0; i < nNodes; i++) { SGPropertyNode * node = nodes[i]; - if (node->getIndex() == index && compare_strings(node->getName(), begin)) + if (node->getIndex() == index && strings_equal(node->getName(), begin)) return i; } #else @@ -402,14 +621,14 @@ find_child (Itr begin, Itr end, int index, const PropertyList& nodes) * Locate the child node with the highest index of the same name */ static int -find_last_child (const char * name, const PropertyList& nodes) +find_last_child (SGPropertyLockExclusive& exclusive, const char * name, const PropertyList& nodes) { size_t nNodes = nodes.size(); int index = -1; for (size_t i = 0; i < nNodes; i++) { SGPropertyNode * node = nodes[i]; - if (compare_strings(node->getName(), name)) + if (strings_equal(node->getName(), name)) { int idx = node->getIndex(); if (idx > index) index = idx; @@ -422,15 +641,17 @@ find_last_child (const char * name, const PropertyList& nodes) * Get first unused index for child nodes with the given name */ static int -first_unused_index( const char * name, +first_unused_index( SGPropertyLockExclusive& exclusive, + const char * name, const PropertyList& nodes, - int min_index ) + int min_index + ) { const char* nameEnd = name + strlen(name); for( int index = min_index; index < std::numeric_limits::max(); ++index ) { - if( find_child(name, nameEnd, index, nodes) < 0 ) + if( find_child(exclusive, name, nameEnd, index, nodes) < 0 ) return index; } @@ -438,39 +659,1083 @@ first_unused_index( const char * name, return -1; } -template -inline SGPropertyNode* -SGPropertyNode::getExistingChild (Itr begin, Itr end, int index) +/* Calls for each item in _listeners. We are careful to skip nullptr +entries in _listeners->items[], which can be created if listeners are removed +while we are iterating. */ +static void forEachListener( + SGPropertyLockExclusive& exclusive, + SGPropertyNode* node, + SGPropertyNodeListeners*& _listeners, + std::function callback + ) { - int pos = find_child(begin, end, index, _children); - if (pos >= 0) - return _children[pos]; - return 0; -} + if (!_listeners) return; -template -SGPropertyNode * -SGPropertyNode::getChildImpl (Itr begin, Itr end, int index, bool create) -{ - SGPropertyNode* node = getExistingChild(begin, end, index); + assert(_listeners->_num_iterators >= 0); + _listeners->_num_iterators += 1; + exclusive.release(); - if (node) { - return node; - } else if (create) { - // REVIEW: Memory Leak - 2,028 (1,976 direct, 52 indirect) bytes in 13 blocks are definitely lost - node = new SGPropertyNode(begin, end, index, this); - _children.push_back(node); - fireChildAdded(node); - return node; - } else { - return 0; + { + SGPropertyLockShared shared(*node); + /* We need to use an index here when iterating _listeners->_items, not an + iterator. This is because a listener may add new listeners, causing the + vector to be reallocated, which would invalidate any iterator. */ + for (size_t i = 0; i < _listeners->_items.size(); ++i) { + auto listener = _listeners->_items[i]; + if (listener) { + shared.release(); + try { + callback(listener); + } + catch (std::exception& e) { + SG_LOG(SG_GENERAL, SG_ALERT, "Ignoring exception from property callback: " << e.what()); + } + shared.acquire(); + } } + } + + exclusive.acquire(); + _listeners->_num_iterators -= 1; + assert(_listeners->_num_iterators >= 0); + + if (_listeners->_num_iterators == 0) { + + /* Remove any items that have been set to nullptr. */ + _listeners->_items.erase( + std::remove( + _listeners->_items.begin(), + _listeners->_items.end(), + (SGPropertyChangeListener*) nullptr + ), + _listeners->_items.end() + ); + if (_listeners->_items.empty()) { + delete _listeners; + _listeners = nullptr; + } + } } + +struct SGPropertyNodeImpl +{ + static bool get_bool(SGPropertyLock& lock, const SGPropertyNode& node) + { + if (node._tied) + return static_cast*>(node._value.val)->getValue(); + else + return node._local_val.bool_val; + } + + static int get_int(SGPropertyLock& lock, const SGPropertyNode& node) + { + if (node._tied) + return static_cast*>(node._value.val)->getValue(); + else + return node._local_val.int_val; + } + + static int get_long(SGPropertyLock& lock, const SGPropertyNode& node) + { + if (node._tied) + return static_cast*>(node._value.val)->getValue(); + else + return node._local_val.long_val; + } + + static float get_float(SGPropertyLock& lock, const SGPropertyNode& node) + { + if (node._tied) + return static_cast*>(node._value.val)->getValue(); + else + return node._local_val.float_val; + } + + static double get_double(SGPropertyLock& lock, const SGPropertyNode& node) + { + if (node._tied) + return static_cast*>(node._value.val)->getValue(); + else + return node._local_val.double_val; + } + + static const char* get_string(SGPropertyLock& lock, const SGPropertyNode& node) + { + if (node._tied) + return static_cast*>(node._value.val)->getValue(); + else + return node._local_val.string_val; + } + + static bool + set_bool(SGPropertyLockExclusive& exclusive, SGPropertyNode& node, bool val) + { + bool changed = false; + if (node._tied) { + exclusive.release(); + changed = static_cast*>(node._value.val)->setValue(val); + exclusive.acquire(); + } + else { + changed = true; + node._local_val.bool_val = val; + } + if (changed) { + SGPropertyNodeImpl::fireValueChanged(exclusive, node, &node); + } + return changed; + } + + static bool + set_int(SGPropertyLockExclusive& exclusive, SGPropertyNode& node, int val) + { + bool changed = false; + if (node._tied) { + exclusive.release(); + changed = static_cast*>(node._value.val)->setValue(val); + exclusive.acquire(); + } + else { + changed = true; + node._local_val.int_val = val; + } + if (changed) { + SGPropertyNodeImpl::fireValueChanged(exclusive, node, &node); + } + return changed; + } + + static bool + set_long(SGPropertyLockExclusive& exclusive, SGPropertyNode& node, long val) + { + bool changed = false; + if (node._tied) { + exclusive.release(); + changed = static_cast*>(node._value.val)->setValue(val); + exclusive.acquire(); + } + else { + changed = true; + node._local_val.long_val = val; + } + if (changed) { + SGPropertyNodeImpl::fireValueChanged(exclusive, node, &node); + } + return changed; + } + + static bool + set_float(SGPropertyLockExclusive& exclusive, SGPropertyNode& node, float val) + { + bool changed = false; + if (node._tied) { + exclusive.release(); + changed = static_cast*>(node._value.val)->setValue(val); + exclusive.acquire(); + } + else { + changed = true; + node._local_val.float_val = val; + } + if (changed) { + SGPropertyNodeImpl::fireValueChanged(exclusive, node, &node); + } + return changed; + } + + static bool + set_double(SGPropertyLockExclusive& exclusive, SGPropertyNode& node, double val) + { + bool changed = false; + if (node._tied) { + exclusive.release(); + changed = static_cast*>(node._value.val)->setValue(val); + exclusive.acquire(); + } + else { + changed = true; + node._local_val.double_val = val; + } + if (changed) { + SGPropertyNodeImpl::fireValueChanged(exclusive, node, &node); + } + return changed; + } + + static bool + set_string(SGPropertyLockExclusive& exclusive, SGPropertyNode& node, const char* val) + { + bool changed = false; + if (node._tied) { + exclusive.release(); + changed = static_cast*>(node._value.val)->setValue(val); + exclusive.acquire(); + } + else { + changed = true; + delete [] node._local_val.string_val; + node._local_val.string_val = copy_string(val); + } + if (changed) { + SGPropertyNodeImpl::fireValueChanged(exclusive, node, &node); + } + return changed; + } + + static SGPropertyNode* + getChildImplCreate(SGPropertyLockExclusive& exclusive, SGPropertyNode& node0, const char* begin, const char* end, int index) + { + SGPropertyNode* node = getExistingChild(exclusive, node0, begin, end, index); + + if (node) { + return node; + } + else { + // REVIEW: Memory Leak - 2,028 (1,976 direct, 52 indirect) bytes in 13 blocks are definitely lost + node = new SGPropertyNode(begin, end, index, &node0); + node0._children.push_back(node); + exclusive.release(); + node0.fireChildAdded(node); + exclusive.acquire(); + return node; + } + } + + static SGPropertyNode* + getExistingChild(SGPropertyLock& lock, SGPropertyNode& node, const char* begin, const char* end, int index) + { + int pos = find_child(lock, begin, end, index, node._children); + if (pos >= 0) + return node._children[pos]; + return 0; + } + + static SGPropertyNode* + getChildImpl(SGPropertyLockShared& shared, SGPropertyNode& node, const char* begin, const char* end, int index) + { + return getExistingChild(shared, node, begin, end, index); + } + + static SGPropertyNode* + getChildImpl(SGPropertyNode& node, const char* begin, const char* end, int index, bool create) + { + if (create) { + SGPropertyLockExclusive exclusive(node); + return getChildImplCreate(exclusive, node, begin, end, index); + } + else { + SGPropertyLockShared shared(node); + return getChildImpl(shared, node, begin, end, index); + } + } + + /* Get the value as a string. */ + static const char * + make_string(SGPropertyLock& lock, const SGPropertyNode& node) + { + if (!getAttribute(lock, node, SGPropertyNode::READ)) + return ""; + switch (node._type) { + case props::ALIAS: + { + SGPropertyNode* p = node._value.alias; + lock.release(); + const char* ret = p->getStringValue(); + lock.acquire(); + return ret; + } + case props::BOOL: + return get_bool(lock, node) ? "true" : "false"; + case props::STRING: + case props::UNSPECIFIED: + return get_string(lock, node); + case props::NONE: + return ""; + default: + break; + } + stringstream sstr; + switch (node._type) { + case props::INT: + sstr << get_int(lock, node); + break; + case props::LONG: + sstr << get_long(lock, node); + break; + case props::FLOAT: + sstr << get_float(lock, node); + break; + case props::DOUBLE: + sstr << std::setprecision(10) << get_double(lock, node); + break; + case props::EXTENDED: + { + props::Type realType = node._value.val->getType(); + // Perhaps this should be done for all types? + if (realType == props::VEC3D || realType == props::VEC4D) + sstr.precision(10); + static_cast(node._value.val)->printOn(sstr); + } + break; + default: + return ""; + } + node._buffer = sstr.str(); + return node._buffer.c_str(); + } + + /** + * Trace a write access for a property. + */ + static void + trace_write (SGPropertyLockExclusive& exclusive, const SGPropertyNode& node) + { + const char* value = SGPropertyNodeImpl::make_string(exclusive, node); + bool own = exclusive.m_own; + if (own) exclusive.release(); + { + SG_LOG(SG_GENERAL, SG_ALERT, "TRACE: Write node " << node.getPath() + << ", value \"" << value << '"'); + } + if (own) exclusive.acquire(); + } + + /** + * Trace a read access for a property. + * + * Note that this releases and re-acquires . + */ + static void + trace_read(SGPropertyLock& lock, const SGPropertyNode& node) + { + // This is tricky; we need to release the lock because getPath() + // will need to acquire locks of parent nodes, and claiming a + // parent/grandparent/... node's lock after a child node's lock can deadlock. + // + // So we temporarily release . But this could break calling code in + // subtle ways - *this could be modified by other threads. + // + // Hopefully we are only called for specific debugging purposes. + // + const char* value = SGPropertyNodeImpl::make_string(lock, node); + lock.release(); + SG_LOG(SG_GENERAL, SG_ALERT, "TRACE: Read node " << node.getPath() + << ", value \"" << value << '"'); + lock.acquire(); + } + + static bool getAttribute (SGPropertyLock& lock, const SGPropertyNode& node, SGPropertyNode::Attribute attr) + { + return ((node._attr & attr) != 0); + } + + static void setAttribute(SGPropertyLockExclusive& exclusive, SGPropertyNode& node, SGPropertyNode::Attribute attr, bool state) + { + (state ? node._attr |= attr : node._attr &= ~attr); + } + + static void setAttributes(SGPropertyLockExclusive& exclusive, SGPropertyNode& node, int attr) + { + node._attr = attr; + } + + static int getAttributes(SGPropertyLock& lock, SGPropertyNode& node) + { + return node._attr; + } + + static void + clearValue(SGPropertyLockExclusive& exclusive, SGPropertyNode& node) + { + if (node._type == props::ALIAS) { + node.put(node._value.alias); + node._value.alias = 0; + } + else if (node._type != props::NONE) { + switch (node._type) { + case props::BOOL: + node._local_val.bool_val = SGRawValue::DefaultValue(); + break; + case props::INT: + node._local_val.int_val = SGRawValue::DefaultValue(); + break; + case props::LONG: + node._local_val.long_val = SGRawValue::DefaultValue(); + break; + case props::FLOAT: + node._local_val.float_val = SGRawValue::DefaultValue(); + break; + case props::DOUBLE: + node._local_val.double_val = SGRawValue::DefaultValue(); + break; + case props::STRING: + case props::UNSPECIFIED: + if (!node._tied) { + delete [] node._local_val.string_val; + } + node._local_val.string_val = 0; + break; + default: // avoid compiler warning + break; + } + delete node._value.val; + node._value.val = 0; + } + node._tied = false; + node._type = props::NONE; + } + + static const char * + getStringValue(SGPropertyLock& lock, const SGPropertyNode& node) + { + // This is inherantly unsafe. + // Shortcut for common case + if (node._attr == (SGPropertyNode::READ|SGPropertyNode::WRITE) && node._type == props::STRING) + return get_string(lock, node); + + if (getAttribute(lock, node, SGPropertyNode::TRACE_READ)) + trace_read(lock, node); + if (!getAttribute(lock, node, SGPropertyNode::READ)) + return SGRawValue::DefaultValue(); + return make_string(lock, node); + } + + static bool + getBoolValue(SGPropertyLock& lock, const SGPropertyNode& node) + { + // Shortcut for common case + if (node._attr == (SGPropertyNode::READ|SGPropertyNode::WRITE) && node._type == props::BOOL) + return get_bool(lock, node); + + if (getAttribute(lock, node, SGPropertyNode::TRACE_READ)) + trace_read(lock, node); // Releases+acquire . + if (!getAttribute(lock, node, SGPropertyNode::READ)) + return SGRawValue::DefaultValue(); + switch (node._type) { + case props::ALIAS: + { + SGPropertyNode* p = node._value.alias; + lock.release(); + return p->getBoolValue(); + } + case props::BOOL: + return get_bool(lock, node); + case props::INT: + return get_int(lock, node) == 0 ? false : true; + case props::LONG: + return get_long(lock, node) == 0L ? false : true; + case props::FLOAT: + return get_float(lock, node) == 0.0 ? false : true; + case props::DOUBLE: + return get_double(lock, node) == 0.0L ? false : true; + case props::STRING: + case props::UNSPECIFIED: + return (strings_equal(get_string(lock, node), "true") || getDoubleValue(lock, node) != 0.0L); + case props::NONE: + default: + return SGRawValue::DefaultValue(); + } + } + + static int + getIntValue(SGPropertyLock& lock, const SGPropertyNode& node) + { + // Shortcut for common case + if (node._attr == (SGPropertyNode::READ|SGPropertyNode::WRITE) && node._type == props::INT) + return get_int(lock, node); + + if (getAttribute(lock, node, SGPropertyNode::TRACE_READ)) + trace_read(lock, node); + if (!getAttribute(lock, node, SGPropertyNode::READ)) + return SGRawValue::DefaultValue(); + switch (node._type) { + case props::ALIAS: + { + SGPropertyNode* p = node._value.alias; + lock.release(); + return p->getIntValue(); + } + case props::BOOL: + return int(get_bool(lock, node)); + case props::INT: + return get_int(lock, node); + case props::LONG: + return int(get_long(lock, node)); + case props::FLOAT: + return int(get_float(lock, node)); + case props::DOUBLE: + return int(get_double(lock, node)); + case props::STRING: + case props::UNSPECIFIED: + return atoi(get_string(lock, node)); + case props::NONE: + default: + return SGRawValue::DefaultValue(); + } + } + + static long + getLongValue(SGPropertyLock& lock, const SGPropertyNode& node) + { + // Shortcut for common case + if (node._attr == (SGPropertyNode::READ|SGPropertyNode::WRITE) && node._type == props::LONG) + return get_long(lock, node); + + if (getAttribute(lock, node, SGPropertyNode::TRACE_READ)) + trace_read(lock, node); + if (!getAttribute(lock, node, SGPropertyNode::READ)) + return SGRawValue::DefaultValue(); + switch (node._type) { + case props::ALIAS: + { + SGPropertyNode* p = node._value.alias; + lock.release(); + return p->getLongValue(); + } + case props::BOOL: + return long(get_bool(lock, node)); + case props::INT: + return long(get_int(lock, node)); + case props::LONG: + return get_long(lock, node); + case props::FLOAT: + return long(get_float(lock, node)); + case props::DOUBLE: + return long(get_double(lock, node)); + case props::STRING: + case props::UNSPECIFIED: + return strtol(get_string(lock, node), 0, 0); + case props::NONE: + default: + return SGRawValue::DefaultValue(); + } + } + + static float + getFloatValue(SGPropertyLock& lock, const SGPropertyNode& node) + { + // Shortcut for common case + if (node._attr == (SGPropertyNode::READ|SGPropertyNode::WRITE) && node._type == props::FLOAT) + return get_float(lock, node); + + if (getAttribute(lock, node, SGPropertyNode::TRACE_READ)) + trace_read(lock, node); + if (!getAttribute(lock, node, SGPropertyNode::READ)) + return SGRawValue::DefaultValue(); + switch (node._type) { + case props::ALIAS: + { + SGPropertyNode* p = node._value.alias; + lock.release(); + return p->getFloatValue(); + } + case props::BOOL: + return float(get_bool(lock, node)); + case props::INT: + return float(get_int(lock, node)); + case props::LONG: + return float(get_long(lock, node)); + case props::FLOAT: + return get_float(lock, node); + case props::DOUBLE: + return float(get_double(lock, node)); + case props::STRING: + case props::UNSPECIFIED: + return atof(get_string(lock, node)); + case props::NONE: + default: + return SGRawValue::DefaultValue(); + } + } + + static double + getDoubleValue(SGPropertyLock& lock, const SGPropertyNode& node) + { + // Shortcut for common case + if (node._attr == (SGPropertyNode::READ|SGPropertyNode::WRITE) && node._type == props::DOUBLE) + return get_double(lock, node); + + if (getAttribute(lock, node, SGPropertyNode::TRACE_READ)) + trace_read(lock, node); + if (!getAttribute(lock, node, SGPropertyNode::READ)) + return SGRawValue::DefaultValue(); + + switch (node._type) { + case props::ALIAS: + { + SGPropertyNode* p = node._value.alias; + lock.release(); + return p->getDoubleValue(); + } + case props::BOOL: + return double(get_bool(lock, node)); + case props::INT: + return double(get_int(lock, node)); + case props::LONG: + return double(get_long(lock, node)); + case props::FLOAT: + return double(get_float(lock, node)); + case props::DOUBLE: + return get_double(lock, node); + case props::STRING: + case props::UNSPECIFIED: + return strtod(get_string(lock, node), 0); + case props::NONE: + default: + return SGRawValue::DefaultValue(); + } + } + + static bool + setBoolValue(SGPropertyLockExclusive& exclusive, SGPropertyNode& node, bool value) + { + // Shortcut for common case + if (node._attr == (SGPropertyNode::READ|SGPropertyNode::WRITE) && node._type == props::BOOL) + return set_bool(exclusive, node, value); + + bool result = false; + if (!getAttribute(exclusive, node, SGPropertyNode::WRITE)) return false; + if (node._type == props::NONE || node._type == props::UNSPECIFIED) { + clearValue(exclusive, node); + node._tied = false; + node._type = props::BOOL; + } + + switch (node._type) { + case props::ALIAS: + { + SGPropertyNode* p = node._value.alias; + exclusive.release(); + result = p->setBoolValue(value); + exclusive.acquire(); + break; + } + case props::BOOL: + result = set_bool(exclusive, node, value); + break; + case props::INT: + result = set_int(exclusive, node, int(value)); + break; + case props::LONG: + result = set_long(exclusive, node, long(value)); + break; + case props::FLOAT: + result = set_float(exclusive, node, float(value)); + break; + case props::DOUBLE: + result = set_double(exclusive, node, double(value)); + break; + case props::STRING: + case props::UNSPECIFIED: + result = set_string(exclusive, node, value ? "true" : "false"); + break; + case props::NONE: + default: + break; + } + + if (getAttribute(exclusive, node, SGPropertyNode::TRACE_WRITE)) { + trace_write(exclusive, node); + } + return result; + } + + static bool + setIntValue(SGPropertyLockExclusive& exclusive, SGPropertyNode& node, int value) + { + // Shortcut for common case + if (node._attr == (SGPropertyNode::READ|SGPropertyNode::WRITE) && node._type == props::INT) + return set_int(exclusive, node, value); + + bool result = false; + if (!getAttribute(exclusive, node, SGPropertyNode::WRITE)) return false; + if (node._type == props::NONE || node._type == props::UNSPECIFIED) { + clearValue(exclusive, node); + node._type = props::INT; + node._local_val.int_val = 0; + } + + switch (node._type) { + case props::ALIAS: + { + SGPropertyNode* p = node._value.alias; + exclusive.release(); + result = p->setIntValue(value); + exclusive.acquire(); + break; + } + case props::BOOL: + result = set_bool(exclusive, node, value == 0 ? false : true); + break; + case props::INT: + result = set_int(exclusive, node, value); + break; + case props::LONG: + result = set_long(exclusive, node, long(value)); + break; + case props::FLOAT: + result = set_float(exclusive, node, float(value)); + break; + case props::DOUBLE: + result = set_double(exclusive, node, double(value)); + break; + case props::STRING: + case props::UNSPECIFIED: { + char buf[128]; + sprintf(buf, "%d", value); + result = set_string(exclusive, node, buf); + break; + } + case props::NONE: + default: + break; + } + + if (getAttribute(exclusive, node, SGPropertyNode::TRACE_WRITE)) + trace_write(exclusive, node); + return result; + } + + static bool + setLongValue(SGPropertyLockExclusive& exclusive, SGPropertyNode& node, long value) + { + // Shortcut for common case + if (node._attr == (SGPropertyNode::READ|SGPropertyNode::WRITE) && node._type == props::LONG) + return set_long(exclusive, node, value); + + bool result = false; + if (!getAttribute(exclusive, node, SGPropertyNode::WRITE)) return false; + if (node._type == props::NONE || node._type == props::UNSPECIFIED) { + clearValue(exclusive, node); + node._type = props::LONG; + node._local_val.long_val = 0L; + } + + switch (node._type) { + case props::ALIAS: + { + SGPropertyNode* p = node._value.alias; + exclusive.release(); + result = p->setLongValue(value); + exclusive.acquire(); + break; + } + case props::BOOL: + result = set_bool(exclusive, node, value == 0L ? false : true); + break; + case props::INT: + result = set_int(exclusive, node, int(value)); + break; + case props::LONG: + result = set_long(exclusive, node, value); + break; + case props::FLOAT: + result = set_float(exclusive, node, float(value)); + break; + case props::DOUBLE: + result = set_double(exclusive, node, double(value)); + break; + case props::STRING: + case props::UNSPECIFIED: { + char buf[128]; + sprintf(buf, "%ld", value); + result = set_string(exclusive, node, buf); + break; + } + case props::NONE: + default: + break; + } + + if (getAttribute(exclusive, node, SGPropertyNode::TRACE_WRITE)) + trace_write(exclusive, node); + return result; + } + + static bool + setFloatValue(SGPropertyLockExclusive& exclusive, SGPropertyNode& node, float value) + { + // Shortcut for common case + if (node._attr == (SGPropertyNode::READ|SGPropertyNode::WRITE) && node._type == props::FLOAT) + return set_float(exclusive, node, value); + + bool result = false; + if (!getAttribute(exclusive, node, SGPropertyNode::WRITE)) return false; + if (node._type == props::NONE || node._type == props::UNSPECIFIED) { + clearValue(exclusive, node); + node._type = props::FLOAT; + node._local_val.float_val = 0; + } + + switch (node._type) { + case props::ALIAS: + { + SGPropertyNode* p = node._value.alias; + exclusive.release(); + result = p->setFloatValue(value); + exclusive.acquire(); + break; + } + case props::BOOL: + result = set_bool(exclusive, node, value == 0.0 ? false : true); + break; + case props::INT: + result = set_int(exclusive, node, int(value)); + break; + case props::LONG: + result = set_long(exclusive, node, long(value)); + break; + case props::FLOAT: + result = set_float(exclusive, node, value); + break; + case props::DOUBLE: + result = set_double(exclusive, node, double(value)); + break; + case props::STRING: + case props::UNSPECIFIED: { + char buf[128]; + sprintf(buf, "%f", value); + result = set_string(exclusive, node, buf); + break; + } + case props::NONE: + default: + break; + } + + if (getAttribute(exclusive, node, SGPropertyNode::TRACE_WRITE)) + trace_write(exclusive, node); + return result; + } + + static bool + setDoubleValue(SGPropertyLockExclusive& exclusive, SGPropertyNode& node, double value) + { + // Shortcut for common case + if (node._attr == (SGPropertyNode::READ|SGPropertyNode::WRITE) && node._type == props::DOUBLE) + return set_double(exclusive, node, value); + + bool result = false; + if (!getAttribute(exclusive, node, SGPropertyNode::WRITE)) return false; + if (node._type == props::NONE || node._type == props::UNSPECIFIED) { + clearValue(exclusive, node); + node._local_val.double_val = value; + node._type = props::DOUBLE; + } + + switch (node._type) { + case props::ALIAS: + { + SGPropertyNode* p = node._value.alias; + exclusive.release(); + result = p->setDoubleValue(value); + exclusive.acquire(); + break; + } + case props::BOOL: + result = set_bool(exclusive, node, value == 0.0L ? false : true); + break; + case props::INT: + result = set_int(exclusive, node, int(value)); + break; + case props::LONG: + result = set_long(exclusive, node, long(value)); + break; + case props::FLOAT: + result = set_float(exclusive, node, float(value)); + break; + case props::DOUBLE: + result = set_double(exclusive, node, value); + break; + case props::STRING: + case props::UNSPECIFIED: { + char buf[128]; + sprintf(buf, "%f", value); + result = set_string(exclusive, node, buf); + break; + } + case props::NONE: + default: + break; + } + + if (getAttribute(exclusive, node, SGPropertyNode::TRACE_WRITE)) + trace_write(exclusive, node); + return result; + } + + static bool + setStringValue(SGPropertyLockExclusive& exclusive, SGPropertyNode& node, const char * value) + { + // Shortcut for common case + if (node._attr == (SGPropertyNode::READ|SGPropertyNode::WRITE) && node._type == props::STRING) + return set_string(exclusive, node, value); + + bool result = false; + if (!getAttribute(exclusive, node, SGPropertyNode::WRITE)) return false; + if (node._type == props::NONE || node._type == props::UNSPECIFIED) { + clearValue(exclusive, node); + node._type = props::STRING; + } + + switch (node._type) { + case props::ALIAS: + { + SGPropertyNode* p = node._value.alias; + exclusive.release(); + result = p->setStringValue(value); + exclusive.acquire(); + break; + } + case props::BOOL: + result = set_bool(exclusive, node, (strings_equal(value, "true") + || atoi(value)) ? true : false); + break; + case props::INT: + result = set_int(exclusive, node, atoi(value)); + break; + case props::LONG: + result = set_long(exclusive, node, strtol(value, 0, 0)); + break; + case props::FLOAT: + result = set_float(exclusive, node, atof(value)); + break; + case props::DOUBLE: + result = set_double(exclusive, node, strtod(value, 0)); + break; + case props::STRING: + case props::UNSPECIFIED: + result = set_string(exclusive, node, value); + break; + case props::EXTENDED: + { + stringstream sstr(value); + static_cast(node._value.val)->readFrom(sstr); + } + break; + case props::NONE: + default: + break; + } + + if (getAttribute(exclusive, node, SGPropertyNode::TRACE_WRITE)) + trace_write(exclusive, node); + return result; + } + + static bool + setStringValue(SGPropertyLockExclusive& exclusive, SGPropertyNode& node, const std::string& value) + { + return setStringValue(exclusive, node, value.c_str()); + } + + static props::Type + getType(SGPropertyLock& lock, const SGPropertyNode& node) + { + if (node._type == props::ALIAS) { + SGPropertyNode* n = node._value.alias; + lock.release(); + props::Type ret = n->getType(); + lock.acquire(); + return ret; + } + else if (node._type == props::EXTENDED) + return node._value.val->getType(); + return node._type; + } + + static bool hasValue(SGPropertyLock& lock, const SGPropertyNode& node) + { + return node._type != simgear::props::NONE; + } + + static void + fireValueChanged (SGPropertyLockExclusive& exclusive, SGPropertyNode& self, SGPropertyNode * node) + { + forEachListener( + exclusive, + &self, + self._listeners, + [&](SGPropertyChangeListener* listener) { listener->valueChanged(node);} + ); + SGPropertyNode* parent = self._parent; + if (parent) { + exclusive.release(); + { + SGPropertyLockExclusive parent_exclusive(*parent); + fireValueChanged(parent_exclusive, *parent, node); + } + exclusive.acquire(); + } + } + + static void + fireChildAdded (SGPropertyLockExclusive& exclusive, SGPropertyNode& self, SGPropertyNode* parent, SGPropertyNode* child) + { + forEachListener( + exclusive, + &self, + self._listeners, + [&](SGPropertyChangeListener* listener) { listener->childAdded(parent, child);} + ); + SGPropertyNode* p = self._parent; + if (p) { + exclusive.release(); + { + SGPropertyLockExclusive p_exclusive(*p); + fireChildAdded(p_exclusive, *p, parent, child); + } + exclusive.acquire(); + } + } + + static void + fireChildRemoved (SGPropertyLockExclusive& exclusive, SGPropertyNode& self, SGPropertyNode* parent, SGPropertyNode* child) + { + forEachListener( + exclusive, + &self, + self._listeners, + [&](SGPropertyChangeListener* listener) { listener->childRemoved(parent, child); } + ); + SGPropertyNode* p = self._parent; + if (p) { + exclusive.release(); + { + SGPropertyLockExclusive p_exclusive(*p); + fireChildRemoved(p_exclusive, *p, parent, child); + } + exclusive.acquire(); + } + } + + static PropertyList + getChildren(SGPropertyLock& lock, const SGPropertyNode& node, const char* name) + { + PropertyList children; + size_t max = node._children.size(); + + for (size_t i = 0; i < max; i++) + if (strings_equal(node._children[i]->getName(), name)) + children.push_back(node._children[i]); + + sort(children.begin(), children.end(), CompareIndices()); + return children; + } + + static simgear::PropertyList + getChildren(SGPropertyLock& lock, const SGPropertyNode& node, const std::string& name) + { + return getChildren(lock, node, name.c_str()); + } + +}; + + template SGPropertyNode* -find_node_aux(SGPropertyNode * current, SplitItr& itr, bool create, - int last_index) +find_node_aux(SGPropertyNode * current, SplitItr& itr, bool create, int last_index) { typedef typename SplitItr::value_type Range; // Run off the end of the list @@ -489,8 +1754,8 @@ find_node_aux(SGPropertyNode * current, SplitItr& itr, bool create, if (equals(name, ".")) return find_node_aux(current, ++itr, create, last_index); if (equals(name, "..")) { - SGPropertyNode * parent = current->getParent(); - if (parent == 0) { + SGPropertyNode* parent = current->getParent(); + if (!parent) { SG_LOG(SG_GENERAL, SG_ALERT, "attempt to move past root with '..' node " << current->getName()); return nullptr; } @@ -533,9 +1798,12 @@ find_node_aux(SGPropertyNode * current, SplitItr& itr, bool create, } } } - return find_node_aux(current->getChildImpl(name.begin(), name.end(), - index, create), itr, create, - last_index); + return find_node_aux( + SGPropertyNodeImpl::getChildImpl(*current, name.begin(), name.end(), index, create), + itr, + create, + last_index + ); } // Internal function for parsing property paths. last_index provides @@ -602,273 +1870,11 @@ SGPropertyNode *find_node(SGPropertyNode *current, const Range &path, // Private methods from SGPropertyNode (may be inlined for speed). //////////////////////////////////////////////////////////////////////// -inline bool -SGPropertyNode::get_bool () const -{ - if (_tied) - return static_cast*>(_value.val)->getValue(); - else - return _local_val.bool_val; -} - -inline int -SGPropertyNode::get_int () const -{ - if (_tied) - return (static_cast*>(_value.val))->getValue(); - else - return _local_val.int_val; -} - -inline long -SGPropertyNode::get_long () const -{ - if (_tied) - return static_cast*>(_value.val)->getValue(); - else - return _local_val.long_val; -} - -inline float -SGPropertyNode::get_float () const -{ - if (_tied) - return static_cast*>(_value.val)->getValue(); - else - return _local_val.float_val; -} - -inline double -SGPropertyNode::get_double () const -{ - if (_tied) - return static_cast*>(_value.val)->getValue(); - else - return _local_val.double_val; -} - -inline const char * -SGPropertyNode::get_string () const -{ - if (_tied) - return static_cast*>(_value.val)->getValue(); - else - return _local_val.string_val; -} - -inline bool -SGPropertyNode::set_bool (bool val) -{ - if (_tied) { - if (static_cast*>(_value.val)->setValue(val)) { - fireValueChanged(); - return true; - } else { - return false; - } - } else { - _local_val.bool_val = val; - fireValueChanged(); - return true; - } -} - -inline bool -SGPropertyNode::set_int (int val) -{ - if (_tied) { - if (static_cast*>(_value.val)->setValue(val)) { - fireValueChanged(); - return true; - } else { - return false; - } - } else { - _local_val.int_val = val; - fireValueChanged(); - return true; - } -} - -inline bool -SGPropertyNode::set_long (long val) -{ - if (_tied) { - if (static_cast*>(_value.val)->setValue(val)) { - fireValueChanged(); - return true; - } else { - return false; - } - } else { - _local_val.long_val = val; - fireValueChanged(); - return true; - } -} - -inline bool -SGPropertyNode::set_float (float val) -{ - if (_tied) { - if (static_cast*>(_value.val)->setValue(val)) { - fireValueChanged(); - return true; - } else { - return false; - } - } else { - _local_val.float_val = val; - fireValueChanged(); - return true; - } -} - -inline bool -SGPropertyNode::set_double (double val) -{ - if (_tied) { - if (static_cast*>(_value.val)->setValue(val)) { - fireValueChanged(); - return true; - } else { - return false; - } - } else { - _local_val.double_val = val; - fireValueChanged(); - return true; - } -} - -inline bool -SGPropertyNode::set_string (const char * val) -{ - if (_tied) { - if (static_cast*>(_value.val)->setValue(val)) { - fireValueChanged(); - return true; - } else { - return false; - } - } else { - delete [] _local_val.string_val; - _local_val.string_val = copy_string(val); - fireValueChanged(); - return true; - } -} - void SGPropertyNode::clearValue () { - if (_type == props::ALIAS) { - put(_value.alias); - _value.alias = 0; - } else if (_type != props::NONE) { - switch (_type) { - case props::BOOL: - _local_val.bool_val = SGRawValue::DefaultValue(); - break; - case props::INT: - _local_val.int_val = SGRawValue::DefaultValue(); - break; - case props::LONG: - _local_val.long_val = SGRawValue::DefaultValue(); - break; - case props::FLOAT: - _local_val.float_val = SGRawValue::DefaultValue(); - break; - case props::DOUBLE: - _local_val.double_val = SGRawValue::DefaultValue(); - break; - case props::STRING: - case props::UNSPECIFIED: - if (!_tied) { - delete [] _local_val.string_val; - } - _local_val.string_val = 0; - break; - default: // avoid compiler warning - break; - } - delete _value.val; - _value.val = 0; - } - _tied = false; - _type = props::NONE; -} - - -/** - * Get the value as a string. - */ -const char * -SGPropertyNode::make_string () const -{ - if (!getAttribute(READ)) - return ""; - switch (_type) { - case props::ALIAS: - return _value.alias->getStringValue(); - case props::BOOL: - return get_bool() ? "true" : "false"; - case props::STRING: - case props::UNSPECIFIED: - return get_string(); - case props::NONE: - return ""; - default: - break; - } - stringstream sstr; - switch (_type) { - case props::INT: - sstr << get_int(); - break; - case props::LONG: - sstr << get_long(); - break; - case props::FLOAT: - sstr << get_float(); - break; - case props::DOUBLE: - sstr << std::setprecision(10) << get_double(); - break; - case props::EXTENDED: - { - props::Type realType = _value.val->getType(); - // Perhaps this should be done for all types? - if (realType == props::VEC3D || realType == props::VEC4D) - sstr.precision(10); - static_cast(_value.val)->printOn(sstr); - } - break; - default: - return ""; - } - _buffer = sstr.str(); - return _buffer.c_str(); -} - -/** - * Trace a write access for a property. - */ -void -SGPropertyNode::trace_write () const -{ - SG_LOG(SG_GENERAL, SG_ALERT, "TRACE: Write node " << getPath() - << ", value \"" << make_string() << '"'); -} - -/** - * Trace a read access for a property. - */ -void -SGPropertyNode::trace_read () const -{ - SG_LOG(SG_GENERAL, SG_ALERT, "TRACE: Read node " << getPath() - << ", value \"" << make_string() << '"'); + SGPropertyLockExclusive exclusive(*this); + SGPropertyNodeImpl::clearValue(exclusive, *this); } //////////////////////////////////////////////////////////////////////// @@ -886,7 +1892,7 @@ const int SGPropertyNode::LAST_USED_ATTRIBUTE = LISTENER_SAFE; */ SGPropertyNode::SGPropertyNode () : _index(0), - _parent(0), + _parent(nullptr), _type(props::NONE), _tied(false), _attr(READ|WRITE), @@ -894,6 +1900,16 @@ SGPropertyNode::SGPropertyNode () { _local_val.string_val = 0; _value.val = 0; + if (0) std::cerr << __FILE__ << ":" << __LINE__ << ":" + << " SGPropertyNode()" + << " this=" << this + << " _name=" << _name + << " SGReferenced::count(this)=" << SGReferenced::count(this) + << " SGReferenced::shared(this)=" << SGReferenced::shared(this) + << "\n"; + if (this == (SGPropertyNode*) 0x7fffffffd6a0) { + std::cerr << __FILE__ << ":" << __LINE__ << ": ***\n"; + } } @@ -901,52 +1917,62 @@ SGPropertyNode::SGPropertyNode () * Copy constructor. */ SGPropertyNode::SGPropertyNode (const SGPropertyNode &node) - : SGReferenced(node), + : + //SGWeakReferenced(node), + SGReferenced(node), _index(node._index), _name(node._name), - _parent(0), // don't copy the parent + _parent(nullptr), // don't copy the parent _type(node._type), _tied(node._tied), _attr(node._attr), _listeners(0) // CHECK!! { - _local_val.string_val = 0; - _value.val = 0; - if (_type == props::NONE) - return; - if (_type == props::ALIAS) { - _value.alias = node._value.alias; - get(_value.alias); - _tied = false; - return; - } - if (_tied || _type == props::EXTENDED) { - _value.val = node._value.val->clone(); - return; - } - switch (_type) { - case props::BOOL: - set_bool(node.get_bool()); - break; - case props::INT: - set_int(node.get_int()); - break; - case props::LONG: - set_long(node.get_long()); - break; - case props::FLOAT: - set_float(node.get_float()); - break; - case props::DOUBLE: - set_double(node.get_double()); - break; - case props::STRING: - case props::UNSPECIFIED: - set_string(node.get_string()); - break; - default: - break; - } + SGPropertyLockShared shared(node); + SGPropertyLockExclusive exclusive(*this); + _local_val.string_val = 0; + _value.val = 0; + if (0) std::cerr << __FILE__ << ":" << __LINE__ << ":" + << " SGPropertyNode()" + << " this=" << this + << " SGReferenced::count(this)=" << SGReferenced::count(this) + << " SGReferenced::shared(this)=" << SGReferenced::shared(this) + << "\n"; + if (_type == props::NONE) + return; + if (_type == props::ALIAS) { + _value.alias = node._value.alias; + get(_value.alias); + _tied = false; + return; + } + if (_tied || _type == props::EXTENDED) { + _value.val = node._value.val->clone(); + return; + } + switch (_type) { + case props::BOOL: + SGPropertyNodeImpl::set_bool(exclusive, *this, SGPropertyNodeImpl::get_bool(shared, node)); + break; + case props::INT: + SGPropertyNodeImpl::set_int(exclusive, *this, SGPropertyNodeImpl::get_int(shared, node)); + break; + case props::LONG: + SGPropertyNodeImpl::set_long(exclusive, *this, SGPropertyNodeImpl::get_long(shared, node)); + break; + case props::FLOAT: + SGPropertyNodeImpl::set_float(exclusive, *this, SGPropertyNodeImpl::get_float(shared, node)); + break; + case props::DOUBLE: + SGPropertyNodeImpl::set_double(exclusive, *this, SGPropertyNodeImpl::get_double(shared, node)); + break; + case props::STRING: + case props::UNSPECIFIED: + SGPropertyNodeImpl::set_string(exclusive, *this, SGPropertyNodeImpl::get_string(shared, node)); + break; + default: + break; + } } @@ -956,7 +1982,7 @@ SGPropertyNode::SGPropertyNode (const SGPropertyNode &node) template SGPropertyNode::SGPropertyNode (Itr begin, Itr end, int index, - SGPropertyNode * parent) + SGPropertyNode* parent) : _index(index), _name(begin, end), _parent(parent), @@ -969,11 +1995,18 @@ SGPropertyNode::SGPropertyNode (Itr begin, Itr end, _value.val = 0; if (!validateName(_name)) throw std::invalid_argument(string{"plain name expected instead of '"} + _name + '\''); + if (0) std::cerr << __FILE__ << ":" << __LINE__ << ":" + << " SGPropertyNode()" + << " this=" << this + << " _name=" << _name + << " SGReferenced::count(this)=" << SGReferenced::count(this) + << " SGReferenced::shared(this)=" << SGReferenced::shared(this) + << "\n"; } SGPropertyNode::SGPropertyNode( const std::string& name, int index, - SGPropertyNode * parent) + SGPropertyNode* parent) : _index(index), _name(name), _parent(parent), @@ -994,9 +2027,8 @@ SGPropertyNode::SGPropertyNode( const std::string& name, */ SGPropertyNode::~SGPropertyNode () { - // zero out all parent pointers, else they might be dangling for (unsigned i = 0; i < _children.size(); ++i) - _children[i]->_parent = 0; + _children[i]->_parent = nullptr; clearValue(); if (_listeners) { @@ -1016,9 +2048,11 @@ SGPropertyNode::alias (SGPropertyNode * target) { if (target && (_type != props::ALIAS) && (!_tied)) { - /* loop protection: check alias chain; must not contain self */ - for (auto p = target; p; p = ((p->_type == props::ALIAS) ? p->_value.alias : nullptr)) { + /* loop protection: check alias chain; must not contain self */ + for (auto p = target; p; ) { if (p == this) return false; + SGPropertyLockShared target_shared(*p); + p = (p->_type == props::ALIAS) ? p->_value.alias : nullptr; } clearValue(); @@ -1058,11 +2092,15 @@ SGPropertyNode::alias (SGPropertyNode * target) * Alias to another node by path. */ bool -SGPropertyNode::alias (const char * path) +SGPropertyNode::alias(const char * path) { return alias(getNode(path, true)); } +bool SGPropertyNode::alias (const std::string& path) +{ + return alias(path.c_str()); +} /** * Remove an alias. @@ -1070,12 +2108,18 @@ SGPropertyNode::alias (const char * path) bool SGPropertyNode::unalias () { + SGPropertyLockExclusive exclusive(*this); if (_type != props::ALIAS) return false; - clearValue(); + SGPropertyNodeImpl::clearValue(exclusive, *this); return true; } +bool SGPropertyNode::isAlias () const +{ + SGPropertyLockShared shared(*this); + return _type == simgear::props::ALIAS; +} /** * Get the target of an alias. @@ -1083,6 +2127,7 @@ SGPropertyNode::unalias () SGPropertyNode * SGPropertyNode::getAliasTarget () { + SGPropertyLockShared shared(*this); return (_type == props::ALIAS ? _value.alias : 0); } @@ -1090,6 +2135,7 @@ SGPropertyNode::getAliasTarget () const SGPropertyNode * SGPropertyNode::getAliasTarget () const { + SGPropertyLockShared shared(*this); return (_type == props::ALIAS ? _value.alias : 0); } @@ -1099,29 +2145,37 @@ SGPropertyNode::getAliasTarget () const SGPropertyNode * SGPropertyNode::addChild(const char * name, int min_index, bool append) { + SGPropertyLockExclusive exclusive(*this); int pos = append - ? std::max(find_last_child(name, _children) + 1, min_index) - : first_unused_index(name, _children, min_index); + ? std::max(find_last_child(exclusive, name, _children) + 1, min_index) + : first_unused_index(exclusive, name, _children, min_index); SGPropertyNode_ptr node; // REVIEW: Memory Leak - 152 bytes in 1 blocks are definitely lost node = new SGPropertyNode(name, name + strlen(name), pos, this); _children.push_back(node); - fireChildAdded(node); + SGPropertyNodeImpl::fireChildAdded(exclusive, *this, this /*parent*/, node); return node; } +SGPropertyNode* +SGPropertyNode::addChild(const std::string& name, int min_index, bool append) +{ + return addChild(name.c_str(), min_index, append); +} + SGPropertyNode_ptr SGPropertyNode::addChild(SGPropertyNode_ptr node, const std::string& name, int min_index, bool append) { + SGPropertyLockExclusive exclusive(*this); int pos = append - ? std::max(find_last_child(name.c_str(), _children) + 1, min_index) - : first_unused_index(name.c_str(), _children, min_index); + ? std::max(find_last_child(exclusive, name.c_str(), _children) + 1, min_index) + : first_unused_index(exclusive, name.c_str(), _children, min_index); node->_name = name; node->_parent = this; node->_index = pos; _children.push_back(node); - fireChildAdded(node); + SGPropertyNodeImpl::fireChildAdded(exclusive, *this, this /*parent*/, node); return node; } @@ -1134,6 +2188,7 @@ SGPropertyNode::addChildren( const std::string& name, int min_index, bool append ) { + SGPropertyLockExclusive exclusive(*this); simgear::PropertyList nodes; std::set used_indices; @@ -1152,7 +2207,7 @@ SGPropertyNode::addChildren( const std::string& name, else { // If we don't want to fill the holes just find last node - min_index = std::max(find_last_child(name.c_str(), _children) + 1, min_index); + min_index = std::max(find_last_child(exclusive, name.c_str(), _children) + 1, min_index); } for( int index = min_index; @@ -1178,10 +2233,11 @@ SGPropertyNode::addChildren( const std::string& name, SGPropertyNode * SGPropertyNode::getChild (int position) { + SGPropertyLockShared shared(*this); if (position >= 0 && position < nChildren()) return _children[position]; else - return 0; + return nullptr; } @@ -1191,21 +2247,74 @@ SGPropertyNode::getChild (int position) const SGPropertyNode * SGPropertyNode::getChild (int position) const { + SGPropertyLockShared shared(*this); if (position >= 0 && position < nChildren()) return _children[position]; else return 0; } +bool SGPropertyNode::hasValue() const +{ + SGPropertyLockShared shared(*this); + return SGPropertyNodeImpl::hasValue(shared, *this); +} + + +const char * SGPropertyNode::getName () const +{ + SGPropertyLockShared shared(*this); + return _name.c_str(); +} + +const std::string& SGPropertyNode::getNameString () const +{ + SGPropertyLockShared shared(*this); + return _name; +} +int SGPropertyNode::getIndex () const +{ + SGPropertyLockShared shared(*this); + return _index; +} + + +SGPropertyNode* SGPropertyNode::getParent() +{ + SGPropertyLockShared shared(*this); + return _parent/*.lock()*/; +} + +const SGPropertyNode* SGPropertyNode::getParent() const +{ + return _parent/*.lock()*/; +} + + unsigned int SGPropertyNode::getPosition() const { - if (_parent == nullptr) { - return 0; - } + SGPropertyNode* parent = _parent/*.lock()*/; + if (!parent) return 0; + SGPropertyLockShared parent_shared(*parent); + auto it = std::find(parent->_children.begin(), parent->_children.end(), this); + assert(it != parent->_children.end()); + return std::distance(parent->_children.begin(), it); +} - auto it = std::find(_parent->_children.begin(), _parent->_children.end(), this); - assert(it != _parent->_children.end()); - return std::distance(_parent->_children.begin(), it); +int SGPropertyNode::nChildren () const +{ + SGPropertyLockShared shared(*this); + return (int)_children.size(); +} + +bool SGPropertyNode::hasChild (const char * name, int index) const +{ + return getChild(name, index) != nullptr; +} + +bool SGPropertyNode::hasChild (const std::string& name, int index) const +{ + return getChild(name, index) != nullptr; } /** @@ -1215,7 +2324,16 @@ unsigned int SGPropertyNode::getPosition() const SGPropertyNode * SGPropertyNode::getChild (const char * name, int index, bool create) { - return getChildImpl(name, name + strlen(name), index, create); + if (create) + { + SGPropertyLockExclusive exclusive(*this); + return SGPropertyNodeImpl::getChildImplCreate(exclusive, *this, name, name + strlen(name), index); + } + else + { + SGPropertyLockShared shared(*this); + return SGPropertyNodeImpl::getChildImpl(shared, *this, name, name + strlen(name), index); + } } SGPropertyNode * @@ -1226,21 +2344,41 @@ SGPropertyNode::getChild (const std::string& name, int index, bool create) int pos = find_child(n, n + strlen(n), index, _children); if (pos >= 0) { return _children[pos]; + } + else if (create) { + // REVIEW: Memory Leak - 12,862 (11,856 direct, 1,006 indirect) bytes in 78 blocks are definitely lost + SGPropertyNode* node = new SGPropertyNode(name, index, this); + // REVIEW: Memory Leak - 104,647 (8 direct, 104,639 indirect) bytes in 1 blocks are definitely lost + _children.push_back(node); + fireChildAdded(node); + return node; + } + else { + return nullptr; + } #else - SGPropertyNode* node = getExistingChild(name.begin(), name.end(), index); - if (node) { - return node; -#endif - } else if (create) { - // REVIEW: Memory Leak - 12,862 (11,856 direct, 1,006 indirect) bytes in 78 blocks are definitely lost - SGPropertyNode* node = new SGPropertyNode(name, index, this); - // REVIEW: Memory Leak - 104,647 (8 direct, 104,639 indirect) bytes in 1 blocks are definitely lost - _children.push_back(node); - fireChildAdded(node); + if (create) + { + SGPropertyLockExclusive exclusive(*this); + SGPropertyNode* node = SGPropertyNodeImpl::getExistingChild(exclusive, *this, /*name.begin(), name.end()*/ name.c_str(), name.c_str() + name.size(), index); + if (node) { return node; } else { - return 0; + // REVIEW: Memory Leak - 12,862 (11,856 direct, 1,006 indirect) bytes in 78 blocks are definitely lost + node = new SGPropertyNode(name, index, this); + // REVIEW: Memory Leak - 104,647 (8 direct, 104,639 indirect) bytes in 1 blocks are definitely lost + _children.push_back(node); + SGPropertyNodeImpl::fireChildAdded(exclusive, *this, this /*parent*/, node); + return node; } + } + else + { + SGPropertyLockShared shared(*this); + SGPropertyNode* node = SGPropertyNodeImpl::getExistingChild(shared, *this, name.c_str(), name.c_str() + name.size(), index); + return node; + } +#endif } /** @@ -1249,56 +2387,79 @@ SGPropertyNode::getChild (const std::string& name, int index, bool create) const SGPropertyNode * SGPropertyNode::getChild (const char * name, int index) const { - int pos = find_child(name, name + strlen(name), index, _children); + SGPropertyLockShared shared(*this); + int pos = find_child(shared, name, name + strlen(name), index, _children); if (pos >= 0) return _children[pos]; else - return 0; + return nullptr; } - -/** - * Get all children with the same name (but different indices). - */ -PropertyList -SGPropertyNode::getChildren (const char * name) const +const SGPropertyNode * SGPropertyNode::getChild (const std::string& name, int index) const { - PropertyList children; - size_t max = _children.size(); + return getChild(name.c_str(), index); +} - for (size_t i = 0; i < max; i++) - if (compare_strings(_children[i]->getName(), name)) - children.push_back(_children[i]); +PropertyList +SGPropertyNode::getChildren(const char * name) const +{ + SGPropertyLockShared shared(*this); + return SGPropertyNodeImpl::getChildren(shared, *this, name); +} - sort(children.begin(), children.end(), CompareIndices()); - return children; +simgear::PropertyList SGPropertyNode::getChildren (const std::string& name) const +{ + return getChildren(name.c_str()); } //------------------------------------------------------------------------------ bool SGPropertyNode::removeChild(SGPropertyNode* node) { - if( node->_parent != this ) - return false; + SGPropertyLockExclusive exclusive(*this); + SGPropertyLockExclusive exclusive_node(*node); - PropertyList::iterator it = - std::find(_children.begin(), _children.end(), node); - if( it == _children.end() ) + /*SGSharedPtr p = node->_parent.lock(); + if (p != this) { + assert(0); return false; - - eraseChild(it); + }*/ + if (node->_parent != this) { + assert(0); + return false; + } + auto it = std::find(_children.begin(), _children.end(), node); + if (it == _children.end()) { + assert(0); + return false; + } + SGPropertyNodeImpl::setAttribute(exclusive_node, *node, REMOVED, true); + SGPropertyNodeImpl::clearValue(exclusive_node, *node); + node->_parent = nullptr; + + exclusive_node.release(); + SGPropertyNodeImpl::fireChildRemoved(exclusive, *this, this /*parent*/, node); + + // Need to find again because fireChildRemoved() will have temporarily + // released our exclusive lock. + it = std::find(_children.begin(), _children.end(), node); + _children.erase(it); return true; } //------------------------------------------------------------------------------ SGPropertyNode_ptr SGPropertyNode::removeChild(int pos) { - if (pos < 0 || pos >= (int)_children.size()) - return SGPropertyNode_ptr(); - - return eraseChild(_children.begin() + pos); + SGPropertyNode_ptr child; + { + SGPropertyLockShared shared(*this); + if (pos < 0 || pos >= (int)_children.size()) + return SGPropertyNode_ptr(); + child = _children[pos]; + } + removeChild(child); + return child; } - /** * Remove a child node */ @@ -1306,12 +2467,21 @@ SGPropertyNode_ptr SGPropertyNode::removeChild(const char * name, int index) { SGPropertyNode_ptr ret; - int pos = find_child(name, name + strlen(name), index, _children); + int pos; + { + SGPropertyLockShared shared(*this); + pos = find_child(shared, name, name + strlen(name), index, _children); + } if (pos >= 0) ret = removeChild(pos); return ret; } +SGPropertyNode_ptr SGPropertyNode::removeChild(const std::string& name, int index) +{ + return removeChild(name.c_str(), index); +} + /** * Remove all children with the specified name. @@ -1320,34 +2490,50 @@ PropertyList SGPropertyNode::removeChildren(const char * name) { PropertyList children; + { + SGPropertyLockShared shared(*this); - for (int pos = static_cast(_children.size() - 1); pos >= 0; pos--) - if (compare_strings(_children[pos]->getName(), name)) - children.push_back(removeChild(pos)); - - sort(children.begin(), children.end(), CompareIndices()); + for (int pos = static_cast(_children.size() - 1); pos >= 0; pos--) { + if (strings_equal(_children[pos]->getName(), name)) { + children.push_back(_children[pos]); + } + } + sort(children.begin(), children.end(), CompareIndices()); + } + + for (SGPropertyNode_ptr child: children) { + removeChild(child); + } return children; } +simgear::PropertyList SGPropertyNode::removeChildren(const std::string& name) +{ + return removeChildren(name.c_str()); +} + void SGPropertyNode::removeAllChildren() { - for(unsigned i = 0; i < _children.size(); ++i) + // Inefficient but simple and hopefully safe. + PropertyList children; { - SGPropertyNode_ptr& node = _children[i]; - node->_parent = 0; - node->setAttribute(REMOVED, true); - node->clearValue(); - fireChildRemoved(node); + SGPropertyLockShared shared(*this); + children = _children; + } + for (SGPropertyNode* child: children) { + removeChild(child); } - - _children.clear(); } std::string SGPropertyNode::getDisplayName (bool simplify) const { - std::string display_name = _name; + std::string display_name; + { + SGPropertyLockShared shared(*this); + display_name = _name; + } if (_index != 0 || !simplify) { stringstream sstr; sstr << '[' << _index << ']'; @@ -1359,526 +2545,110 @@ SGPropertyNode::getDisplayName (bool simplify) const std::string SGPropertyNode::getPath (bool simplify) const { - typedef std::vector PList; - PList pathList; - for (const SGPropertyNode* node = this; node->_parent; node = node->_parent) - pathList.push_back(node); + std::vector nodes; + const SGPropertyNode* node = this; + for(;;) { + SGPropertyNode* parent; + { + SGPropertyLockShared shared(*node); + parent = node->_parent; + } + if (!parent) break; + nodes.push_back(node); + node = parent; + } + std::string result; - for (PList::reverse_iterator itr = pathList.rbegin(), - rend = pathList.rend(); - itr != rend; - ++itr) { + for (auto it = nodes.rbegin(); it != nodes.rend(); ++it) { result += '/'; - result += (*itr)->getDisplayName(simplify); + result += (*it)->getDisplayName(simplify); } return result; } -props::Type -SGPropertyNode::getType () const +bool SGPropertyNode::getAttribute (Attribute attr) const { - if (_type == props::ALIAS) - return _value.alias->getType(); - else if (_type == props::EXTENDED) - return _value.val->getType(); - else - return _type; + SGPropertyLockShared shared(*this); + return SGPropertyNodeImpl::getAttribute(shared, *this, attr); +} + +void SGPropertyNode::setAttribute (Attribute attr, bool state) +{ + SGPropertyLockExclusive exclusive(*this); + SGPropertyNodeImpl::setAttribute(exclusive, *this, attr, state); +} + +int SGPropertyNode::getAttributes() const +{ + SGPropertyLockShared shared(*this); + return _attr; +} + +void SGPropertyNode::setAttributes(int attr) +{ + SGPropertyLockExclusive exclusive(*this); + SGPropertyNodeImpl::setAttributes(exclusive, *this, attr); +} + +props::Type +SGPropertyNode::getType() const +{ + SGPropertyLockShared shared(*this); + return SGPropertyNodeImpl::getType(shared, *this); } bool -SGPropertyNode::getBoolValue () const +SGPropertyNode::getBoolValue() const { - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == props::BOOL) - return get_bool(); - - if (getAttribute(TRACE_READ)) - trace_read(); - if (!getAttribute(READ)) - return SGRawValue::DefaultValue(); - switch (_type) { - case props::ALIAS: - return _value.alias->getBoolValue(); - case props::BOOL: - return get_bool(); - case props::INT: - return get_int() == 0 ? false : true; - case props::LONG: - return get_long() == 0L ? false : true; - case props::FLOAT: - return get_float() == 0.0 ? false : true; - case props::DOUBLE: - return get_double() == 0.0L ? false : true; - case props::STRING: - case props::UNSPECIFIED: - return (compare_strings(get_string(), "true") || getDoubleValue() != 0.0L); - case props::NONE: - default: - return SGRawValue::DefaultValue(); - } + SGPropertyLockShared shared(*this); + return SGPropertyNodeImpl::getBoolValue(shared, *this); } int -SGPropertyNode::getIntValue () const +SGPropertyNode::getIntValue() const { - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == props::INT) - return get_int(); - - if (getAttribute(TRACE_READ)) - trace_read(); - if (!getAttribute(READ)) - return SGRawValue::DefaultValue(); - switch (_type) { - case props::ALIAS: - return _value.alias->getIntValue(); - case props::BOOL: - return int(get_bool()); - case props::INT: - return get_int(); - case props::LONG: - return int(get_long()); - case props::FLOAT: - return int(get_float()); - case props::DOUBLE: - return int(get_double()); - case props::STRING: - case props::UNSPECIFIED: - return atoi(get_string()); - case props::NONE: - default: - return SGRawValue::DefaultValue(); - } + SGPropertyLockShared shared(*this); + return SGPropertyNodeImpl::getIntValue(shared, *this); } long -SGPropertyNode::getLongValue () const +SGPropertyNode::getLongValue() const { - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == props::LONG) - return get_long(); - - if (getAttribute(TRACE_READ)) - trace_read(); - if (!getAttribute(READ)) - return SGRawValue::DefaultValue(); - switch (_type) { - case props::ALIAS: - return _value.alias->getLongValue(); - case props::BOOL: - return long(get_bool()); - case props::INT: - return long(get_int()); - case props::LONG: - return get_long(); - case props::FLOAT: - return long(get_float()); - case props::DOUBLE: - return long(get_double()); - case props::STRING: - case props::UNSPECIFIED: - return strtol(get_string(), 0, 0); - case props::NONE: - default: - return SGRawValue::DefaultValue(); - } + SGPropertyLockShared shared(*this); + return SGPropertyNodeImpl::getLongValue(shared, *this); } float SGPropertyNode::getFloatValue () const { - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == props::FLOAT) - return get_float(); - - if (getAttribute(TRACE_READ)) - trace_read(); - if (!getAttribute(READ)) - return SGRawValue::DefaultValue(); - switch (_type) { - case props::ALIAS: - return _value.alias->getFloatValue(); - case props::BOOL: - return float(get_bool()); - case props::INT: - return float(get_int()); - case props::LONG: - return float(get_long()); - case props::FLOAT: - return get_float(); - case props::DOUBLE: - return float(get_double()); - case props::STRING: - case props::UNSPECIFIED: - return atof(get_string()); - case props::NONE: - default: - return SGRawValue::DefaultValue(); - } + SGPropertyLockShared shared(*this); + return SGPropertyNodeImpl::getFloatValue(shared, *this); } double -SGPropertyNode::getDoubleValue () const +SGPropertyNode::getDoubleValue() const { - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == props::DOUBLE) - return get_double(); - - if (getAttribute(TRACE_READ)) - trace_read(); - if (!getAttribute(READ)) - return SGRawValue::DefaultValue(); - - switch (_type) { - case props::ALIAS: - return _value.alias->getDoubleValue(); - case props::BOOL: - return double(get_bool()); - case props::INT: - return double(get_int()); - case props::LONG: - return double(get_long()); - case props::FLOAT: - return double(get_float()); - case props::DOUBLE: - return get_double(); - case props::STRING: - case props::UNSPECIFIED: - return strtod(get_string(), 0); - case props::NONE: - default: - return SGRawValue::DefaultValue(); - } + SGPropertyLockShared shared(*this); + return SGPropertyNodeImpl::getDoubleValue(shared, *this); } + const char * -SGPropertyNode::getStringValue () const +SGPropertyNode::getStringValue() const { - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == props::STRING) - return get_string(); - - if (getAttribute(TRACE_READ)) - trace_read(); - if (!getAttribute(READ)) - return SGRawValue::DefaultValue(); - return make_string(); -} - -bool -SGPropertyNode::setBoolValue (bool value) -{ - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == props::BOOL) - return set_bool(value); - - bool result = false; - TEST_WRITE; - if (_type == props::NONE || _type == props::UNSPECIFIED) { - clearValue(); - _tied = false; - _type = props::BOOL; - } - - switch (_type) { - case props::ALIAS: - result = _value.alias->setBoolValue(value); - break; - case props::BOOL: - result = set_bool(value); - break; - case props::INT: - result = set_int(int(value)); - break; - case props::LONG: - result = set_long(long(value)); - break; - case props::FLOAT: - result = set_float(float(value)); - break; - case props::DOUBLE: - result = set_double(double(value)); - break; - case props::STRING: - case props::UNSPECIFIED: - result = set_string(value ? "true" : "false"); - break; - case props::NONE: - default: - break; - } - - if (getAttribute(TRACE_WRITE)) - trace_write(); - return result; -} - -bool -SGPropertyNode::setIntValue (int value) -{ - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == props::INT) - return set_int(value); - - bool result = false; - TEST_WRITE; - if (_type == props::NONE || _type == props::UNSPECIFIED) { - clearValue(); - _type = props::INT; - _local_val.int_val = 0; - } - - switch (_type) { - case props::ALIAS: - result = _value.alias->setIntValue(value); - break; - case props::BOOL: - result = set_bool(value == 0 ? false : true); - break; - case props::INT: - result = set_int(value); - break; - case props::LONG: - result = set_long(long(value)); - break; - case props::FLOAT: - result = set_float(float(value)); - break; - case props::DOUBLE: - result = set_double(double(value)); - break; - case props::STRING: - case props::UNSPECIFIED: { - char buf[128]; - sprintf(buf, "%d", value); - result = set_string(buf); - break; - } - case props::NONE: - default: - break; - } - - if (getAttribute(TRACE_WRITE)) - trace_write(); - return result; -} - -bool -SGPropertyNode::setLongValue (long value) -{ - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == props::LONG) - return set_long(value); - - bool result = false; - TEST_WRITE; - if (_type == props::NONE || _type == props::UNSPECIFIED) { - clearValue(); - _type = props::LONG; - _local_val.long_val = 0L; - } - - switch (_type) { - case props::ALIAS: - result = _value.alias->setLongValue(value); - break; - case props::BOOL: - result = set_bool(value == 0L ? false : true); - break; - case props::INT: - result = set_int(int(value)); - break; - case props::LONG: - result = set_long(value); - break; - case props::FLOAT: - result = set_float(float(value)); - break; - case props::DOUBLE: - result = set_double(double(value)); - break; - case props::STRING: - case props::UNSPECIFIED: { - char buf[128]; - sprintf(buf, "%ld", value); - result = set_string(buf); - break; - } - case props::NONE: - default: - break; - } - - if (getAttribute(TRACE_WRITE)) - trace_write(); - return result; -} - -bool -SGPropertyNode::setFloatValue (float value) -{ - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == props::FLOAT) - return set_float(value); - - bool result = false; - TEST_WRITE; - if (_type == props::NONE || _type == props::UNSPECIFIED) { - clearValue(); - _type = props::FLOAT; - _local_val.float_val = 0; - } - - switch (_type) { - case props::ALIAS: - result = _value.alias->setFloatValue(value); - break; - case props::BOOL: - result = set_bool(value == 0.0 ? false : true); - break; - case props::INT: - result = set_int(int(value)); - break; - case props::LONG: - result = set_long(long(value)); - break; - case props::FLOAT: - result = set_float(value); - break; - case props::DOUBLE: - result = set_double(double(value)); - break; - case props::STRING: - case props::UNSPECIFIED: { - char buf[128]; - sprintf(buf, "%f", value); - result = set_string(buf); - break; - } - case props::NONE: - default: - break; - } - - if (getAttribute(TRACE_WRITE)) - trace_write(); - return result; -} - -bool -SGPropertyNode::setDoubleValue (double value) -{ - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == props::DOUBLE) - return set_double(value); - - bool result = false; - TEST_WRITE; - if (_type == props::NONE || _type == props::UNSPECIFIED) { - clearValue(); - _local_val.double_val = value; - _type = props::DOUBLE; - } - - switch (_type) { - case props::ALIAS: - result = _value.alias->setDoubleValue(value); - break; - case props::BOOL: - result = set_bool(value == 0.0L ? false : true); - break; - case props::INT: - result = set_int(int(value)); - break; - case props::LONG: - result = set_long(long(value)); - break; - case props::FLOAT: - result = set_float(float(value)); - break; - case props::DOUBLE: - result = set_double(value); - break; - case props::STRING: - case props::UNSPECIFIED: { - char buf[128]; - sprintf(buf, "%f", value); - result = set_string(buf); - break; - } - case props::NONE: - default: - break; - } - - if (getAttribute(TRACE_WRITE)) - trace_write(); - return result; -} - -bool -SGPropertyNode::setStringValue (const char * value) -{ - // Shortcut for common case - if (_attr == (READ|WRITE) && _type == props::STRING) - return set_string(value); - - bool result = false; - TEST_WRITE; - if (_type == props::NONE || _type == props::UNSPECIFIED) { - clearValue(); - _type = props::STRING; - } - - switch (_type) { - case props::ALIAS: - result = _value.alias->setStringValue(value); - break; - case props::BOOL: - result = set_bool((compare_strings(value, "true") - || atoi(value)) ? true : false); - break; - case props::INT: - result = set_int(atoi(value)); - break; - case props::LONG: - result = set_long(strtol(value, 0, 0)); - break; - case props::FLOAT: - result = set_float(atof(value)); - break; - case props::DOUBLE: - result = set_double(strtod(value, 0)); - break; - case props::STRING: - case props::UNSPECIFIED: - result = set_string(value); - break; - case props::EXTENDED: - { - stringstream sstr(value); - static_cast(_value.val)->readFrom(sstr); - } - break; - case props::NONE: - default: - break; - } - - if (getAttribute(TRACE_WRITE)) - trace_write(); - return result; + SGPropertyLockShared shared(*this); + return SGPropertyNodeImpl::getStringValue(shared, *this); } bool SGPropertyNode::setUnspecifiedValue (const char * value) { + SGPropertyLockExclusive exclusive(*this); bool result = false; - TEST_WRITE; + if (!SGPropertyNodeImpl::getAttribute(exclusive, *this, WRITE)) return false; if (_type == props::NONE) { - clearValue(); + SGPropertyNodeImpl::clearValue(exclusive, *this); _type = props::UNSPECIFIED; } props::Type type = _type; @@ -1886,34 +2656,43 @@ SGPropertyNode::setUnspecifiedValue (const char * value) type = _value.val->getType(); switch (type) { case props::ALIAS: - result = _value.alias->setUnspecifiedValue(value); - break; + { + SGPropertyNode* p = _value.alias; + exclusive.release(); + result = p->setUnspecifiedValue(value); + exclusive.acquire(); + break; + } case props::BOOL: - result = set_bool((compare_strings(value, "true") + result = SGPropertyNodeImpl::set_bool(exclusive, *this, (strings_equal(value, "true") || atoi(value)) ? true : false); break; case props::INT: - result = set_int(atoi(value)); + result = SGPropertyNodeImpl::set_int(exclusive, *this, atoi(value)); break; case props::LONG: - result = set_long(strtol(value, 0, 0)); + result = SGPropertyNodeImpl::set_long(exclusive, *this, strtol(value, 0, 0)); break; case props::FLOAT: - result = set_float(atof(value)); + result = SGPropertyNodeImpl::set_float(exclusive, *this, atof(value)); break; case props::DOUBLE: - result = set_double(strtod(value, 0)); + result = SGPropertyNodeImpl::set_double(exclusive, *this, strtod(value, 0)); break; case props::STRING: case props::UNSPECIFIED: - result = set_string(value); + result = SGPropertyNodeImpl::set_string(exclusive, *this, value); break; #if !PROPS_STANDALONE case props::VEC3D: + exclusive.release(); result = static_cast*>(_value.val)->setValue(parseString(value)); + exclusive.acquire(); break; case props::VEC4D: + exclusive.release(); result = static_cast*>(_value.val)->setValue(parseString(value)); + exclusive.acquire(); break; #endif case props::NONE: @@ -1921,10 +2700,86 @@ SGPropertyNode::setUnspecifiedValue (const char * value) break; } - if (getAttribute(TRACE_WRITE)) - trace_write(); + if (SGPropertyNodeImpl::getAttribute(exclusive, *this, TRACE_WRITE)) + SGPropertyNodeImpl::trace_write(exclusive, *this); return result; } + +bool SGPropertyNode::setBoolValue(bool value) +{ + SGPropertyLockExclusive exclusive(*this); + return SGPropertyNodeImpl::setBoolValue(exclusive, *this, value); +} + +bool SGPropertyNode::setIntValue(int value) +{ + SGPropertyLockExclusive exclusive(*this); + return SGPropertyNodeImpl::setIntValue(exclusive, *this, value); +} + +bool SGPropertyNode::setLongValue(long value) +{ + SGPropertyLockExclusive exclusive(*this); + return SGPropertyNodeImpl::setLongValue(exclusive, *this, value); +} + +bool SGPropertyNode::setFloatValue(float value) +{ + SGPropertyLockExclusive exclusive(*this); + return SGPropertyNodeImpl::setFloatValue(exclusive, *this, value); +} + +bool SGPropertyNode::setDoubleValue(double value) +{ + SGPropertyLockExclusive exclusive(*this); + return SGPropertyNodeImpl::setDoubleValue(exclusive, *this, value); +} + +bool SGPropertyNode::setStringValue(const char* value) +{ + SGPropertyLockExclusive exclusive(*this); + return SGPropertyNodeImpl::setStringValue(exclusive, *this, value); +} + +bool SGPropertyNode::setStringValue(const std::string& value) +{ + SGPropertyLockExclusive exclusive(*this); + return SGPropertyNodeImpl::setStringValue(exclusive, *this, value); +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + //------------------------------------------------------------------------------ #if !PROPS_STANDALONE @@ -1933,6 +2788,7 @@ bool SGPropertyNode::interpolate( const std::string& type, double duration, const std::string& easing ) { + if( !_interpolation_mgr ) { SG_LOG(SG_GENERAL, SG_WARN, "No property interpolator available"); @@ -1982,29 +2838,34 @@ simgear::PropertyInterpolationMgr* SGPropertyNode::_interpolation_mgr = 0; //------------------------------------------------------------------------------ std::ostream& SGPropertyNode::printOn(std::ostream& stream) const { - if (!getAttribute(READ)) + SGPropertyLockShared shared(*this); + if (!SGPropertyNodeImpl::getAttribute(shared, *this, READ)) return stream; switch (_type) { case props::ALIAS: - return _value.alias->printOn(stream); + { + SGPropertyNode* p = _value.alias; + shared.release(); + return p->printOn(stream); + } case props::BOOL: - stream << (get_bool() ? "true" : "false"); + stream << (SGPropertyNodeImpl::get_bool(shared, *this) ? "true" : "false"); break; case props::INT: - stream << get_int(); + stream << SGPropertyNodeImpl::get_int(shared, *this); break; case props::LONG: - stream << get_long(); + stream << SGPropertyNodeImpl::get_long(shared, *this); break; case props::FLOAT: - stream << get_float(); + stream << SGPropertyNodeImpl::get_float(shared, *this); break; case props::DOUBLE: - stream << get_double(); + stream << SGPropertyNodeImpl::get_double(shared, *this); break; case props::STRING: case props::UNSPECIFIED: - stream << get_string(); + stream << SGPropertyNodeImpl::get_string(shared, *this); break; case props::EXTENDED: static_cast(_value.val)->printOn(stream); @@ -2017,77 +2878,84 @@ std::ostream& SGPropertyNode::printOn(std::ostream& stream) const return stream; } -template<> -bool SGPropertyNode::tie (const SGRawValue &rawValue, - bool useDefault) +bool SGPropertyNode::isTied () const { + return _tied; +} + +template<> +bool SGPropertyNode::tie(const SGRawValue &rawValue, bool useDefault) +{ + SGPropertyLockExclusive exclusive(*this); if (_type == props::ALIAS || _tied) return false; - useDefault = useDefault && hasValue(); + useDefault = useDefault && SGPropertyNodeImpl::hasValue(exclusive, *this); std::string old_val; if (useDefault) - old_val = getStringValue(); - clearValue(); + old_val = SGPropertyNodeImpl::getStringValue(exclusive, *this); + SGPropertyNodeImpl::clearValue(exclusive, *this); _type = props::STRING; _tied = true; _value.val = rawValue.clone(); if (useDefault) { - int save_attributes = getAttributes(); - setAttribute( WRITE, true ); - setStringValue(old_val.c_str()); - setAttributes( save_attributes ); + int save_attributes = SGPropertyNodeImpl::getAttributes(exclusive, *this); + SGPropertyNodeImpl::setAttribute(exclusive, *this, WRITE, true ); + SGPropertyNodeImpl::setStringValue(exclusive, *this, old_val.c_str()); + SGPropertyNodeImpl::setAttributes(exclusive, *this, save_attributes); } return true; } + bool -SGPropertyNode::untie () +SGPropertyNode::untie() { + SGPropertyLockExclusive exclusive(*this); if (!_tied) return false; switch (_type) { case props::BOOL: { - bool val = getBoolValue(); - clearValue(); + bool val = SGPropertyNodeImpl::getBoolValue(exclusive, *this); + SGPropertyNodeImpl::clearValue(exclusive, *this); _type = props::BOOL; _local_val.bool_val = val; break; } case props::INT: { - int val = getIntValue(); - clearValue(); + int val = SGPropertyNodeImpl::getIntValue(exclusive, *this); + SGPropertyNodeImpl::SGPropertyNodeImpl::clearValue(exclusive, *this); _type = props::INT; _local_val.int_val = val; break; } case props::LONG: { - long val = getLongValue(); - clearValue(); + long val = SGPropertyNodeImpl::SGPropertyNodeImpl::getLongValue(exclusive, *this); + SGPropertyNodeImpl::clearValue(exclusive, *this); _type = props::LONG; _local_val.long_val = val; break; } case props::FLOAT: { - float val = getFloatValue(); - clearValue(); + float val = SGPropertyNodeImpl::getFloatValue(exclusive, *this); + SGPropertyNodeImpl::clearValue(exclusive, *this); _type = props::FLOAT; _local_val.float_val = val; break; } case props::DOUBLE: { - double val = getDoubleValue(); - clearValue(); + double val = SGPropertyNodeImpl::getDoubleValue(exclusive, *this); + SGPropertyNodeImpl::clearValue(exclusive, *this); _type = props::DOUBLE; _local_val.double_val = val; break; } case props::STRING: case props::UNSPECIFIED: { - std::string val = getStringValue(); - clearValue(); + std::string val = SGPropertyNodeImpl::getStringValue(exclusive, *this); + SGPropertyNodeImpl::clearValue(exclusive, *this); _type = props::STRING; _local_val.string_val = copy_string(val.c_str()); break; @@ -2095,7 +2963,7 @@ SGPropertyNode::untie () case props::EXTENDED: { SGRawExtended* val = static_cast(_value.val); _value.val = 0; // Prevent clearValue() from deleting - clearValue(); + SGPropertyNodeImpl::clearValue(exclusive, *this); _type = props::EXTENDED; _value.val = val->makeContainer(); delete val; @@ -2113,19 +2981,16 @@ SGPropertyNode::untie () SGPropertyNode * SGPropertyNode::getRootNode () { - if (_parent == 0) - return this; - else - return _parent->getRootNode(); + SGPropertyLockShared shared(*this); + SGPropertyNode* parent = _parent/*.lock()*/; + shared.release(); + return (parent) ? parent->getRootNode() : this; } const SGPropertyNode * SGPropertyNode::getRootNode () const { - if (_parent == 0) - return this; - else - return _parent->getRootNode(); + return const_cast(this)->getRootNode(); } SGPropertyNode * @@ -2137,10 +3002,11 @@ SGPropertyNode::getNode (const char * relative_path, bool create) return find_node(this, components, 0, create); #else - return find_node(this, - boost::make_iterator_range(relative_path, - relative_path + strlen(relative_path)), - create); + return find_node( + this, + boost::make_iterator_range(relative_path, relative_path + strlen(relative_path)), + create + ); #endif } @@ -2155,23 +3021,41 @@ SGPropertyNode::getNode (const char * relative_path, int index, bool create) return find_node(this, components, 0, create); #else - return find_node(this, - boost::make_iterator_range(relative_path, - relative_path + strlen(relative_path)), - create, index); + return find_node( + this, + boost::make_iterator_range(relative_path, relative_path + strlen(relative_path)), + create, + index + ); #endif } +SGPropertyNode * SGPropertyNode::getNode (const std::string& relative_path, int index, bool create) +{ + return getNode(relative_path.c_str(), index, create); +} + + const SGPropertyNode * SGPropertyNode::getNode (const char * relative_path) const { - return ((SGPropertyNode *)this)->getNode(relative_path, false); + return const_cast(this)->getNode(relative_path, false); +} + +const SGPropertyNode * SGPropertyNode::getNode (const std::string& relative_path) const +{ + return getNode(relative_path.c_str()); } const SGPropertyNode * SGPropertyNode::getNode (const char * relative_path, int index) const { - return ((SGPropertyNode *)this)->getNode(relative_path, index, false); + return const_cast(this)->getNode(relative_path, index, false); +} + +const SGPropertyNode * SGPropertyNode::getNode (const std::string& relative_path, int index) const +{ + return getNode(relative_path.c_str(), index); } //////////////////////////////////////////////////////////////////////// @@ -2186,9 +3070,13 @@ bool SGPropertyNode::hasValue (const char * relative_path) const { const SGPropertyNode * node = getNode(relative_path); - return (node == 0 ? false : node->hasValue()); + return (node) ? node->hasValue() : false; } +bool SGPropertyNode::hasValue (const std::string& relative_path) const +{ + return hasValue(relative_path.c_str()); +} /** * Get the value type for another node. @@ -2197,33 +3085,43 @@ props::Type SGPropertyNode::getType (const char * relative_path) const { const SGPropertyNode * node = getNode(relative_path); - return (node == 0 ? props::UNSPECIFIED : node->getType()); + return (node) ? node->getType() : props::UNSPECIFIED; } +simgear::props::Type SGPropertyNode::getType (const std::string& relative_path) const +{ + return getType(relative_path.c_str()); +} /** * Get a bool value for another node. */ bool -SGPropertyNode::getBoolValue (const char * relative_path, - bool defaultValue) const +SGPropertyNode::getBoolValue (const char * relative_path, bool defaultValue) const { const SGPropertyNode * node = getNode(relative_path); - return (node == 0 ? defaultValue : node->getBoolValue()); + return (node) ? node->getBoolValue() : defaultValue; } +bool SGPropertyNode::getBoolValue (const std::string& relative_path, bool defaultValue) const +{ + return getBoolValue(relative_path.c_str(), defaultValue); +} /** * Get an int value for another node. */ int -SGPropertyNode::getIntValue (const char * relative_path, - int defaultValue) const +SGPropertyNode::getIntValue (const char * relative_path, int defaultValue) const { const SGPropertyNode * node = getNode(relative_path); - return (node == 0 ? defaultValue : node->getIntValue()); + return (node) ? node->getIntValue() : defaultValue; } +int SGPropertyNode::getIntValue (const std::string& relative_path, int defaultValue) const +{ + return getIntValue(relative_path.c_str(), defaultValue); +} /** * Get a long value for another node. @@ -2233,9 +3131,13 @@ SGPropertyNode::getLongValue (const char * relative_path, long defaultValue) const { const SGPropertyNode * node = getNode(relative_path); - return (node == 0 ? defaultValue : node->getLongValue()); + return (node) ? node->getLongValue() : defaultValue; } +long SGPropertyNode::getLongValue (const std::string& relative_path, long defaultValue) const +{ + return getLongValue(relative_path.c_str(), defaultValue); +} /** * Get a float value for another node. @@ -2245,9 +3147,13 @@ SGPropertyNode::getFloatValue (const char * relative_path, float defaultValue) const { const SGPropertyNode * node = getNode(relative_path); - return (node == 0 ? defaultValue : node->getFloatValue()); + return (node) ? node->getFloatValue() : defaultValue; } +float SGPropertyNode::getFloatValue (const std::string& relative_path, float defaultValue) const +{ + return getFloatValue(relative_path.c_str(), defaultValue); +} /** * Get a double value for another node. @@ -2257,9 +3163,13 @@ SGPropertyNode::getDoubleValue (const char * relative_path, double defaultValue) const { const SGPropertyNode * node = getNode(relative_path); - return (node == 0 ? defaultValue : node->getDoubleValue()); + return (node) ? node->getDoubleValue() : defaultValue; } +double SGPropertyNode::getDoubleValue (const std::string& relative_path, double defaultValue) const +{ + return getDoubleValue(relative_path.c_str(), defaultValue); +} /** * Get a string value for another node. @@ -2269,9 +3179,13 @@ SGPropertyNode::getStringValue (const char * relative_path, const char * defaultValue) const { const SGPropertyNode * node = getNode(relative_path); - return (node == 0 ? defaultValue : node->getStringValue()); + return (node) ? node->getStringValue() : defaultValue; } +const char * SGPropertyNode::getStringValue (const std::string& relative_path, const char * defaultValue) const +{ + return getStringValue(relative_path.c_str(), defaultValue); +} /** * Set a bool value for another node. @@ -2282,6 +3196,10 @@ SGPropertyNode::setBoolValue (const char * relative_path, bool value) return getNode(relative_path, true)->setBoolValue(value); } +bool SGPropertyNode::setBoolValue (const std::string& relative_path, bool value) +{ + return setBoolValue(relative_path.c_str(), value); +} /** * Set an int value for another node. @@ -2292,6 +3210,10 @@ SGPropertyNode::setIntValue (const char * relative_path, int value) return getNode(relative_path, true)->setIntValue(value); } +bool SGPropertyNode::setIntValue (const std::string& relative_path, int value) +{ + return setIntValue(relative_path.c_str(), value); +} /** * Set a long value for another node. @@ -2302,6 +3224,10 @@ SGPropertyNode::setLongValue (const char * relative_path, long value) return getNode(relative_path, true)->setLongValue(value); } +bool SGPropertyNode::setLongValue (const std::string& relative_path, long value) +{ + return setLongValue(relative_path.c_str(), value); +} /** * Set a float value for another node. @@ -2312,6 +3238,10 @@ SGPropertyNode::setFloatValue (const char * relative_path, float value) return getNode(relative_path, true)->setFloatValue(value); } +bool SGPropertyNode::setFloatValue (const std::string& relative_path, float value) +{ + return setFloatValue(relative_path.c_str(), value); +} /** * Set a double value for another node. @@ -2322,6 +3252,10 @@ SGPropertyNode::setDoubleValue (const char * relative_path, double value) return getNode(relative_path, true)->setDoubleValue(value); } +bool SGPropertyNode::setDoubleValue (const std::string& relative_path, double value) +{ + return setDoubleValue(relative_path.c_str(), value); +} /** * Set a string value for another node. @@ -2332,13 +3266,26 @@ SGPropertyNode::setStringValue (const char * relative_path, const char * value) return getNode(relative_path, true)->setStringValue(value); } +bool SGPropertyNode::setStringValue(const char * relative_path, const std::string& value) +{ + return setStringValue(relative_path, value.c_str()); +} + +bool SGPropertyNode::setStringValue (const std::string& relative_path, const char * value) +{ + return setStringValue(relative_path.c_str(), value); +} + +bool SGPropertyNode::setStringValue (const std::string& relative_path, const std::string& value) +{ + return setStringValue(relative_path.c_str(), value.c_str()); +} /** * Set an unknown value for another node. */ bool -SGPropertyNode::setUnspecifiedValue (const char * relative_path, - const char * value) +SGPropertyNode::setUnspecifiedValue (const char * relative_path, const char * value) { return getNode(relative_path, true)->setUnspecifiedValue(value); } @@ -2351,101 +3298,145 @@ bool SGPropertyNode::isTied (const char * relative_path) const { const SGPropertyNode * node = getNode(relative_path); - return (node == 0 ? false : node->isTied()); + return (node) ? node->isTied() : false; } +bool SGPropertyNode::isTied (const std::string& relative_path) const +{ + return isTied(relative_path.c_str()); +} /** * Tie a node reached by a relative path, creating it if necessary. */ bool -SGPropertyNode::tie (const char * relative_path, - const SGRawValue &rawValue, - bool useDefault) +SGPropertyNode::tie( + const char* relative_path, + const SGRawValue &rawValue, + bool useDefault + ) { return getNode(relative_path, true)->tie(rawValue, useDefault); } +bool SGPropertyNode::tie (const std::string& relative_path, const SGRawValue &rawValue, bool useDefault) +{ + return tie(relative_path.c_str(), rawValue, useDefault); +} /** * Tie a node reached by a relative path, creating it if necessary. */ bool -SGPropertyNode::tie (const char * relative_path, - const SGRawValue &rawValue, - bool useDefault) +SGPropertyNode::tie( + const char * relative_path, + const SGRawValue &rawValue, + bool useDefault + ) { return getNode(relative_path, true)->tie(rawValue, useDefault); } +bool SGPropertyNode::tie (const std::string& relative_path, const SGRawValue &rawValue, bool useDefault) +{ + return tie(relative_path.c_str(), rawValue, useDefault); +} /** * Tie a node reached by a relative path, creating it if necessary. */ bool -SGPropertyNode::tie (const char * relative_path, - const SGRawValue &rawValue, - bool useDefault) +SGPropertyNode::tie( + const char * relative_path, + const SGRawValue &rawValue, + bool useDefault + ) { return getNode(relative_path, true)->tie(rawValue, useDefault); } +bool SGPropertyNode::tie (const std::string& relative_path, const SGRawValue &rawValue, bool useDefault) +{ + return tie(relative_path.c_str(), rawValue, useDefault); +} /** * Tie a node reached by a relative path, creating it if necessary. */ bool -SGPropertyNode::tie (const char * relative_path, - const SGRawValue &rawValue, - bool useDefault) +SGPropertyNode::tie( + const char* relative_path, + const SGRawValue &rawValue, + bool useDefault + ) { return getNode(relative_path, true)->tie(rawValue, useDefault); } +bool SGPropertyNode::tie (const std::string& relative_path, const SGRawValue &rawValue, bool useDefault) +{ + return tie(relative_path.c_str(), rawValue, useDefault); +} /** * Tie a node reached by a relative path, creating it if necessary. */ bool -SGPropertyNode::tie (const char * relative_path, - const SGRawValue &rawValue, - bool useDefault) +SGPropertyNode::tie( + const char* relative_path, + const SGRawValue &rawValue, + bool useDefault + ) { return getNode(relative_path, true)->tie(rawValue, useDefault); } +bool SGPropertyNode::tie (const std::string& relative_path, const SGRawValue &rawValue, bool useDefault) +{ + return tie(relative_path.c_str(), rawValue, useDefault); +} /** * Tie a node reached by a relative path, creating it if necessary. */ bool -SGPropertyNode::tie (const char * relative_path, - const SGRawValue &rawValue, - bool useDefault) +SGPropertyNode::tie( + const char * relative_path, + const SGRawValue &rawValue, + bool useDefault + ) { return getNode(relative_path, true)->tie(rawValue, useDefault); } +bool SGPropertyNode::tie (const std::string& relative_path, const SGRawValue &rawValue, bool useDefault) +{ + return tie(relative_path.c_str(), rawValue, useDefault); +} /** * Attempt to untie another node reached by a relative path. */ bool -SGPropertyNode::untie (const char * relative_path) +SGPropertyNode::untie(const char * relative_path) { - SGPropertyNode * node = getNode(relative_path); - return (node == 0 ? false : node->untie()); + SGPropertyNode* node = getNode(relative_path); + return (node) ? node->untie() : false; +} + +bool SGPropertyNode::untie (const std::string& relative_path) +{ + return untie(relative_path.c_str()); } void -SGPropertyNode::addChangeListener (SGPropertyChangeListener * listener, - bool initial) +SGPropertyNode::addChangeListener(SGPropertyChangeListener* listener, bool initial) { + SGPropertyLockExclusive exclusive(*this); if (_listeners == 0) // REVIEW: Memory Leak - 32 bytes in 1 blocks are indirectly lost _listeners = new SGPropertyNodeListeners; - std::lock_guard lock(_listeners->_rmutex); /* If there's a nullptr entry (a listener that was unregistered), we overwrite it. This ensures that listeners that routinely unregister+register themselves don't make _listeners->_items grow unnecessarily. Otherwise simply @@ -2461,19 +3452,21 @@ SGPropertyNode::addChangeListener (SGPropertyChangeListener * listener, *it = listener; } listener->register_property(this); - if (initial) + if (initial) { // REVIEW: Memory Leak - 24,928 bytes in 164 blocks are indirectly lost + exclusive.release(); listener->valueChanged(this); + } } void SGPropertyNode::removeChangeListener (SGPropertyChangeListener * listener) { + SGPropertyLockExclusive exclusive(*this); if (_listeners == 0) return; /* We use a std::unique_lock rather than a std::lock_guard because we may need to unlock early. */ - std::unique_lock lock(_listeners->_rmutex); vector::iterator it = find(_listeners->_items.begin(), _listeners->_items.end(), listener); if (it != _listeners->_items.end()) { @@ -2496,24 +3489,25 @@ SGPropertyNode::removeChangeListener (SGPropertyChangeListener * listener) _listeners->_items.erase(it); listener->unregister_property(this); if (_listeners->_items.empty()) { - lock.unlock(); delete _listeners; - _listeners = 0; + _listeners = nullptr; } } } } void -SGPropertyNode::fireValueChanged () +SGPropertyNode::fireValueChanged() { - fireValueChanged(this); + SGPropertyLockExclusive exclusive(*this); + SGPropertyNodeImpl::fireValueChanged(exclusive, *this, this); } void SGPropertyNode::fireChildAdded (SGPropertyNode * child) { - fireChildAdded(this, child); + SGPropertyLockExclusive exclusive(*this); + SGPropertyNodeImpl::fireChildAdded(exclusive, *this, this /*parent*/, child); } void @@ -2521,84 +3515,50 @@ SGPropertyNode::fireCreatedRecursive(bool fire_self) { if( fire_self ) { - _parent->fireChildAdded(this); - - if( _children.empty() && getType() != simgear::props::NONE ) - return fireValueChanged(); + SGPropertyNode* parent = _parent/*.lock()*/; + if (parent) { + parent->fireChildAdded(this); + } + SGPropertyLockExclusive exclusive(*this); + if( _children.empty() && SGPropertyNodeImpl::getType(exclusive, *this) != simgear::props::NONE ) + return SGPropertyNodeImpl::fireValueChanged(exclusive, *this, this); } - for(size_t i = 0; i < _children.size(); ++i) - _children[i]->fireCreatedRecursive(true); + simgear::PropertyList children; + { + SGPropertyLockShared shared(*this); + children = _children; + } + for(size_t i = 0; i < children.size(); ++i) + children[i]->fireCreatedRecursive(true); } void SGPropertyNode::fireChildRemoved (SGPropertyNode * child) { - fireChildRemoved(this, child); + SGPropertyLockExclusive exclusive(*this); + SGPropertyNodeImpl::fireChildRemoved(exclusive, *this, this /*parent*/, child); } void SGPropertyNode::fireChildrenRemovedRecursive() { - for(size_t i = 0; i < _children.size(); ++i) + simgear::PropertyList children; { - SGPropertyNode* child = _children[i]; - fireChildRemoved(this, child); + SGPropertyLockShared shared(*this); + children = _children; + } + for (SGPropertyNode* child: children) + { + fireChildRemoved(child); child->fireChildrenRemovedRecursive(); } } -/* Calls for each item in _listeners. We are careful to skip nullptr -entries in _listeners->items[], which can be created if listeners are removed -while we are iterating. */ -static void forEachListener( - SGPropertyNode* node, - SGPropertyNodeListeners*& _listeners, - std::function callback - ) -{ - if (!_listeners) return; - - std::lock_guard lock(_listeners->_rmutex); - assert(_listeners->_num_iterators >= 0); - _listeners->_num_iterators += 1; - - /* We need to use an index here when iterating _listeners->_items, not an - iterator. This is because a listener may add new listeners, causing the - vector to be reallocated, which would invalidate any iterator. */ - for (size_t i = 0; i < _listeners->_items.size(); ++i) { - auto listener = _listeners->_items[i]; - if (listener) { - try { - callback(listener); - } - catch (std::exception& e) { - SG_LOG(SG_GENERAL, SG_ALERT, "Ignoring exception from property callback: " << e.what()); - } - } - } - - _listeners->_num_iterators -= 1; - assert(_listeners->_num_iterators >= 0); - - if (_listeners->_num_iterators == 0) { - - /* Remove any items that have been set to nullptr. */ - _listeners->_items.erase( - std::remove(_listeners->_items.begin(), _listeners->_items.end(), (SGPropertyChangeListener*) nullptr), - _listeners->_items.end() - ); - if (_listeners->_items.empty()) { - delete _listeners; - _listeners = nullptr; - } - } -} - int SGPropertyNode::nListeners() const { + SGPropertyLockShared shared(*this); if (!_listeners) return 0; - std::lock_guard lock(_listeners->_rmutex); int n = 0; for (auto listener: _listeners->_items) { if (listener) n += 1; @@ -2606,48 +3566,12 @@ int SGPropertyNode::nListeners() const return n; } -void -SGPropertyNode::fireValueChanged (SGPropertyNode * node) -{ - forEachListener( - node, - _listeners, - [&](SGPropertyChangeListener* listener) { listener->valueChanged(node);} - ); - if (_parent != 0) - _parent->fireValueChanged(node); -} - -void -SGPropertyNode::fireChildAdded (SGPropertyNode * parent, - SGPropertyNode * child) -{ - forEachListener( - parent, - _listeners, - [&](SGPropertyChangeListener* listener) { listener->childAdded(parent, child);} - ); - if (_parent != 0) - _parent->fireChildAdded(parent, child); -} - -void -SGPropertyNode::fireChildRemoved (SGPropertyNode * parent, - SGPropertyNode * child) -{ - forEachListener( - parent, - _listeners, - [&](SGPropertyChangeListener* listener) { listener->childRemoved(parent, child);} - ); - if (_parent != 0) - _parent->fireChildRemoved(parent, child); -} - +#if 0 //------------------------------------------------------------------------------ SGPropertyNode_ptr SGPropertyNode::eraseChild(simgear::PropertyList::iterator child) { + ProtectScope lock(this, true /*write*/); SGPropertyNode_ptr node = *child; node->setAttribute(REMOVED, true); node->clearValue(); @@ -2656,6 +3580,7 @@ SGPropertyNode::eraseChild(simgear::PropertyList::iterator child) _children.erase(child); return node; } +#endif //////////////////////////////////////////////////////////////////////// // Implementation of SGPropertyChangeListener. @@ -2762,7 +3687,34 @@ namespace simgear namespace { + #if 0 bool compareNodeValue(const SGPropertyNode& lhs, const SGPropertyNode& rhs) + { + if (&lhs == &rhs) return true; + /* Need to find whether this is above or below in the property tree so + we can take out locks in a safe order. */ + SGPropertyLockShared shared_lhs; + SGPropertyLockShared shared_rhs; + int d = ancestor_compare(&lhs, &rhs); + if (d >= 0) { + // is parent/grandparent/... of . + shared_lhs.assign(lhs); + shared_rhs.assign(rhs); + } + else { + shared_rhs.assign(rhs); + shared_lhs.assign(lhs); + } + return compareNodeValue(shared_lhs, shared_rhs, lhs, rhs); + } + #endif + + bool compareNodeValue( + SGPropertyLockShared& shared_lhs, + SGPropertyLockShared& shared_rhs, + const SGPropertyNode& lhs, + const SGPropertyNode& rhs + ) { props::Type ltype = lhs.getType(); props::Type rtype = rhs.getType(); @@ -2799,6 +3751,30 @@ namespace simgear } } +/* Returns +1 if a is parent of b, +2 if a is grandparent of b, etc. Otherwise +returns 0. We also return 0 if a==b. */ +static int ancestor_distance(SGConstPropertyNode_ptr a, SGConstPropertyNode_ptr b) +{ + int distance = 0; + for(;;) { + b = b->getParent(); + if (!b) return 0; + distance += 1; + if (a == b) return distance; + } +} + +/* Returns +1/+2/+3/... if a is parent/grandparent/... of b. Returns +-1/-2/-3/... if b is parent/grandparent/... of a. */ +static int ancestor_compare(SGConstPropertyNode_ptr a, SGConstPropertyNode_ptr b) +{ + int distance; + distance = ancestor_distance(a, b); + if (distance > 0) return distance; + distance = ancestor_distance(b, a); + if (distance > 0) return -distance; + return 0; +} void SGPropertyNode::copy(SGPropertyNode *to) const { @@ -2806,7 +3782,8 @@ void SGPropertyNode::copy(SGPropertyNode *to) const { for (int i = 0; i < nChildren(); i++) { const SGPropertyNode *child = getChild(i); - SGPropertyNode *to_child = to->getChild(child->getName()); + if (!child) break; // We don't lock child, so getChild() could return 0; + SGPropertyNode* to_child = to->getChild(child->getName()); if (!to_child) to_child = to->addChild(child->getName()); if (child->nChildren()) @@ -2823,34 +3800,62 @@ void SGPropertyNode::copy(SGPropertyNode *to) const to->setValue(getStringValue()); } +SGPropertyNode * SGPropertyNode::getNode (const std::string& relative_path, bool create) +{ + return getNode(relative_path.c_str(), create); +} + + bool SGPropertyNode::compare(const SGPropertyNode& lhs, const SGPropertyNode& rhs) { if (&lhs == &rhs) return true; - int lhsChildren = lhs.nChildren(); - int rhsChildren = rhs.nChildren(); + /* Need to find whether this is above or below in the property tree so + we can take out locks in a safe order. */ + SGPropertyLockShared shared_lhs; + SGPropertyLockShared shared_rhs; + int d = ancestor_compare(&lhs, &rhs); + if (d >= 0) { + // is parent/grandparent/... of . + shared_lhs.assign(lhs); + shared_rhs.assign(rhs); + } + else { + shared_rhs.assign(rhs); + shared_lhs.assign(lhs); + } + int lhsChildren = lhs._children.size(); + int rhsChildren = rhs._children.size(); if (lhsChildren != rhsChildren) return false; + if (lhsChildren == 0) - return compareNodeValue(lhs, rhs); + return compareNodeValue(shared_lhs, shared_rhs, lhs, rhs); + for (size_t i = 0; i < lhs._children.size(); ++i) { const SGPropertyNode* lchild = lhs._children[i]; const SGPropertyNode* rchild = rhs._children[i]; // I'm guessing that the nodes will usually be in the same // order. if (lchild->getIndex() != rchild->getIndex() - || lchild->getNameString() != rchild->getNameString()) { - rchild = 0; - for (PropertyList::const_iterator itr = rhs._children.begin(), - end = rhs._children.end(); - itr != end; - ++itr) + || lchild->getNameString() != rchild->getNameString() + ) + { + /* Search for matching child in rhs. */ + rchild = nullptr; + for (PropertyList::const_iterator itr = rhs._children.begin(); + itr != rhs._children.end(); + ++itr) + { if (lchild->getIndex() == (*itr)->getIndex() - && lchild->getNameString() == (*itr)->getNameString()) { + && lchild->getNameString() == (*itr)->getNameString() + ) + { rchild = *itr; break; } + } if (!rchild) return false; } @@ -2872,50 +3877,309 @@ struct PropertyPlaceLess { } }; + +template +bool SGPropertyNode::tie(const SGRawValue &rawValue, bool useDefault) +{ + SGPropertyLockExclusive exclusive(*this); + using namespace simgear::props; + if (_type == ALIAS || _tied) + return false; + + useDefault = useDefault && SGPropertyNodeImpl::hasValue(exclusive, *this); + T old_val = SGRawValue::DefaultValue(); + if (useDefault) + old_val = getValue(exclusive); + SGPropertyNodeImpl::clearValue(exclusive, *this); + if (PropertyTraits::Internal) + _type = PropertyTraits::type_tag; + else + _type = EXTENDED; + _tied = true; + _value.val = rawValue.clone(); + if (useDefault) { + int save_attributes = _attr; + SGPropertyNodeImpl::setAttribute(exclusive, *this, WRITE, true); + setValue(exclusive, old_val); + _attr = save_attributes; + } + return true; + +} + + + + + +template +T SGPropertyNode::getValue( + SGPropertyLock& lock, + typename std::enable_if::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 (SGPropertyNodeImpl::getAttribute(lock, *this, TRACE_READ)) + SGPropertyNodeImpl::trace_read(lock, *this); + if (!SGPropertyNodeImpl::getAttribute(lock, *this, 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(SGPropertyNodeImpl::make_string(lock, *this)); + default: // avoid compiler warning + break; + } + return SGRawValue::DefaultValue(); +} + +template +T SGPropertyNode::getValue( + typename std::enable_if::Internal>::type* dummy + ) const +{ + SGPropertyLockShared shared(*this); + return getValue(shared, dummy); +} + + +template +inline T +SGPropertyNode::getValue(typename std::enable_if::Internal>::type* dummy) const +{ + return ::getValue(this); +} + +template // TODO use C++11 or traits +std::vector SGPropertyNode::getChildValues(const std::string& name) const +{ + SGPropertyLockShared shared(*this); + const simgear::PropertyList& props = SGPropertyNodeImpl::getChildren(shared, *this, name); + std::vector values( props.size() ); + + for( size_t i = 0; i < props.size(); ++i ) + values[i] = props[i]->getValue(); + + return values; +} + +template +inline std::vector +SGPropertyNode::getChildValues(const std::string& name) const +{ + return getChildValues(name); +} + + +template<> +bool SGPropertyNode::setValue(SGPropertyLockExclusive& exclusive, const bool& val, void* dummy) +{ + return SGPropertyNodeImpl::setBoolValue(exclusive, *this, val); +} + +template<> +bool SGPropertyNode::setValue(SGPropertyLockExclusive& exclusive, const int& val, void* dummy) +{ + return SGPropertyNodeImpl::setIntValue(exclusive, *this, val); +} + +template<> +bool SGPropertyNode::setValue(SGPropertyLockExclusive& exclusive, const long& val, void* dummy) +{ + return SGPropertyNodeImpl::setLongValue(exclusive, *this, val); +} + +template<> +bool SGPropertyNode::setValue(SGPropertyLockExclusive& exclusive, const float& val, void* dummy) +{ + return SGPropertyNodeImpl::setFloatValue(exclusive, *this, val); +} + +template<> +bool SGPropertyNode::setValue(SGPropertyLockExclusive& exclusive, const double& val, void* dummy) +{ + return SGPropertyNodeImpl::setDoubleValue(exclusive, *this, val); +} + + +template +bool SGPropertyNode::setValue( + SGPropertyLockExclusive& exclusive, + const T& val, + typename std::enable_if::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 (SGPropertyNodeImpl::getAttribute(exclusive, *this, WRITE) + && ( + ( + _type == EXTENDED + && _value.val->getType() == PropertyTraits::type_tag + ) + || _type == NONE + || _type == UNSPECIFIED + )) + { + if (_type == NONE || _type == UNSPECIFIED) { + SGPropertyNodeImpl::clearValue(exclusive, *this); + _type = EXTENDED; + _value.val = new SGRawValueContainer(val); + } else { + static_cast*>(_value.val)->setValue(val); + } + if (SGPropertyNodeImpl::getAttribute(exclusive, *this, TRACE_WRITE)) + SGPropertyNodeImpl::trace_write(exclusive, *this); + return true; + } + return false; +} + +template +bool SGPropertyNode::setValue( + const T& val, + typename std::enable_if::Internal>::type* dummy + ) +{ + SGPropertyLockExclusive exclusive(*this); + return setValue(exclusive, val, dummy); +} + +template +inline bool SGPropertyNode::setValue( + const T& val, + typename std::enable_if::Internal>::type* dummy + ) +{ + return ::setValue(this, val); +} + + +// Explicit specialisations and instantiations. +// + +template bool SGPropertyNode::getValue(void*) const; +template int SGPropertyNode::getValue(void*) const; +template long SGPropertyNode::getValue(void*) const; +template float SGPropertyNode::getValue(void*) const; +template double SGPropertyNode::getValue(void*) const; +template const char* SGPropertyNode::getValue(void*) const; +template SGVec3 SGPropertyNode::getValue>(void*) const; +template SGVec4 SGPropertyNode::getValue>(void*) const; + +template<> +bool SGPropertyNode::getValue(SGPropertyLock& lock, void* dummy) const +{ + return SGPropertyNodeImpl::getBoolValue(lock, *this); +} + +template<> +int SGPropertyNode::getValue(SGPropertyLock& lock, void* dummy) const +{ + return SGPropertyNodeImpl::getIntValue(lock, *this); +} + +template<> +long SGPropertyNode::getValue(SGPropertyLock& lock, void* dummy) const +{ + return SGPropertyNodeImpl::getLongValue(lock, *this); +} + +template<> +float SGPropertyNode::getValue(SGPropertyLock& lock, void* dummy) const +{ + return SGPropertyNodeImpl::getFloatValue(lock, *this); +} + +template<> +double SGPropertyNode::getValue(SGPropertyLock& lock, void* dummy) const +{ + return SGPropertyNodeImpl::getDoubleValue(lock, *this); +} + +template<> +const char* SGPropertyNode::getValue(SGPropertyLock& lock, void* dummy) const +{ + return SGPropertyNodeImpl::getStringValue(lock, *this); +} + +template bool SGPropertyNode::setValue(const bool&, void*); +template bool SGPropertyNode::setValue(const int&, void*); +template bool SGPropertyNode::setValue(const long&, void*); +template bool SGPropertyNode::setValue(const float&, void*); +template bool SGPropertyNode::setValue(const double&, void*); +template bool SGPropertyNode::setValue(const char* const&, void*); +template bool SGPropertyNode::setValue(const SGVec3&, void*); +template bool SGPropertyNode::setValue(const SGVec4&, void*); + +template std::vector SGPropertyNode::getChildValues(const std::string& name) const; +template std::vector SGPropertyNode::getChildValues(const std::string& name) const; + +template bool SGPropertyNode::tie(const SGRawValue &rawValue, bool useDefault); +template bool SGPropertyNode::tie(const SGRawValue &rawValue, bool useDefault); +template bool SGPropertyNode::tie(const SGRawValue &rawValue, bool useDefault); +template bool SGPropertyNode::tie(const SGRawValue &rawValue, bool useDefault); +template bool SGPropertyNode::tie(const SGRawValue &rawValue, bool useDefault); + + #if !PROPS_STANDALONE size_t hash_value(const SGPropertyNode& node) { using namespace boost; - if (node.nChildren() == 0) { - switch (node.getType()) { - case props::NONE: - return 0; + SGPropertyLockShared shared(node); - case props::BOOL: - return boost::hash_value(node.getValue()); - case props::INT: - return boost::hash_value(node.getValue()); - case props::LONG: - return boost::hash_value(node.getValue()); - case props::FLOAT: - return boost::hash_value(node.getValue()); - case props::DOUBLE: - return boost::hash_value(node.getValue()); - case props::STRING: - case props::UNSPECIFIED: - { - const char *val = node.getStringValue(); - return boost::hash_range(val, val + strlen(val)); - } - case props::VEC3D: - { - const SGVec3d val = node.getValue(); - return boost::hash_range(&val[0], &val[3]); - } - case props::VEC4D: - { - const SGVec4d val = node.getValue(); - return hash_range(&val[0], &val[4]); - } - case props::ALIAS: // XXX Should we look in aliases? - default: - return 0; + if (node._children.empty()) { + switch (SGPropertyNodeImpl::getType(shared, node)) { + case props::NONE: + return 0; + case props::BOOL: + return boost::hash_value(SGPropertyNodeImpl::getBoolValue(shared, node)); + case props::INT: + return boost::hash_value(SGPropertyNodeImpl::getIntValue(shared, node)); + case props::LONG: + return boost::hash_value(SGPropertyNodeImpl::getLongValue(shared, node)); + case props::FLOAT: + return boost::hash_value(SGPropertyNodeImpl::getFloatValue(shared, node)); + case props::DOUBLE: + return boost::hash_value(SGPropertyNodeImpl::getDoubleValue(shared, node)); + case props::STRING: + case props::UNSPECIFIED: + { + const char *val = SGPropertyNodeImpl::getStringValue(shared, node); + return boost::hash_range(val, val + strlen(val)); + } + case props::VEC3D: + { + const SGVec3d val = node.getValue(shared); + return boost::hash_range(&val[0], &val[3]); + } + case props::VEC4D: + { + const SGVec4d val = node.getValue(shared); + return hash_range(&val[0], &val[4]); + } + case props::ALIAS: // XXX Should we look in aliases? + default: + return 0; } } else { size_t seed = 0; - PropertyList children(node._children.begin(), node._children.end()); + PropertyList children = node._children; //(node._children.begin(), node._children.end()); sort(children.begin(), children.end(), PropertyPlaceLess()); - for (PropertyList::const_iterator itr = children.begin(), + for (PropertyList::const_iterator itr = children.begin(), end = children.end(); itr != end; ++itr) { @@ -2926,6 +4190,7 @@ size_t hash_value(const SGPropertyNode& node) return seed; } } + #endif // end of props.cxx diff --git a/simgear/props/props.hxx b/simgear/props/props.hxx index bba11fd4..eb521747 100644 --- a/simgear/props/props.hxx +++ b/simgear/props/props.hxx @@ -21,7 +21,8 @@ #include #include #include - +#include + #include #if PROPS_STANDALONE # ifndef SG_LOG @@ -38,6 +39,7 @@ #include #include +#include // XXX This whole file should be in the simgear namespace, but I don't // have the guts yet... @@ -47,7 +49,7 @@ using namespace std; namespace simgear { - class PropertyInterpolationMgr; +class PropertyInterpolationMgr; template std::istream& readFrom(std::istream& stream, T& result) @@ -164,12 +166,12 @@ DEFINTERNALPROP(int, INT); DEFINTERNALPROP(long, LONG); DEFINTERNALPROP(float, FLOAT); DEFINTERNALPROP(double, DOUBLE); -DEFINTERNALPROP(const char *, STRING); +DEFINTERNALPROP(const char*, STRING); DEFINTERNALPROP(const char[], STRING); #undef DEFINTERNALPROP -} -} +} // namespace props +} // namespace simgear @@ -292,66 +294,61 @@ class SGRawValue : public SGRawBase { public: - /** - * The default underlying value for this type. - * - * Every raw value has a default; the default is false for a - * boolean, 0 for the various numeric values, and "" for a string. - * If additional types of raw values are added in the future, they - * may need different kinds of default values (such as epoch for a - * date type). The default value is used when creating new values. - */ - static T DefaultValue() - { - return T(); - } + /** + * The default underlying value for this type. + * + * Every raw value has a default; the default is false for a + * boolean, 0 for the various numeric values, and "" for a string. + * If additional types of raw values are added in the future, they + * may need different kinds of default values (such as epoch for a + * date type). The default value is used when creating new values. + */ + static T DefaultValue() + { + return T(); + } + /** + * Constructor. + * + * Use the default value for this type. + */ + SGRawValue() {} - /** - * Constructor. - * - * Use the default value for this type. - */ - SGRawValue () {} + /** + * Destructor. + */ + virtual ~SGRawValue() {} + /** + * Return the underlying value. + * + * @return The actual value for the property. + * @see #setValue + */ + virtual T getValue() const = 0; - /** - * Destructor. - */ - virtual ~SGRawValue () {} + /** + * Assign a new underlying value. + * + * If the new value cannot be set (because this is a read-only + * raw value, or because the new value is not acceptable for + * some reason) this method returns false and leaves the original + * value unchanged. + * + * @param value The actual value for the property. + * @return true if the value was set successfully, false otherwise. + * @see #getValue + */ + virtual bool setValue(T value) = 0; - - /** - * Return the underlying value. - * - * @return The actual value for the property. - * @see #setValue - */ - virtual T getValue () const = 0; - - - /** - * Assign a new underlying value. - * - * If the new value cannot be set (because this is a read-only - * raw value, or because the new value is not acceptable for - * some reason) this method returns false and leaves the original - * value unchanged. - * - * @param value The actual value for the property. - * @return true if the value was set successfully, false otherwise. - * @see #getValue - */ - virtual bool setValue (T value) = 0; - - - /** - * Return the type tag for this raw value type. - */ - virtual simgear::props::Type getType() const - { - return simgear::props::PropertyTraits::type_tag; - } + /** + * Return the type tag for this raw value type. + */ + virtual simgear::props::Type getType() const + { + return simgear::props::PropertyTraits::type_tag; + } }; @@ -362,12 +359,12 @@ public: template<> inline bool SGRawValue::DefaultValue() { - return false; + return false; } -template<> inline const char * SGRawValue::DefaultValue() +template<> inline const char* SGRawValue::DefaultValue() { - return ""; + return ""; } /** @@ -385,49 +382,59 @@ class SGRawValuePointer : public SGRawValue { public: - /** - * Explicit pointer constructor. - * - * Create a new raw value bound to the value of the variable - * referenced by the pointer. - * - * @param ptr The pointer to the variable to which this raw value - * will be bound. - */ - SGRawValuePointer (T * ptr) : _ptr(ptr) {} + /** + * Explicit pointer constructor. + * + * Create a new raw value bound to the value of the variable + * referenced by the pointer. + * + * @param ptr The pointer to the variable to which this raw value + * will be bound. + */ + SGRawValuePointer(T* ptr) : _ptr(ptr) + {} - /** - * Destructor. - */ - virtual ~SGRawValuePointer () {} + /** + * Destructor. + */ + virtual ~SGRawValuePointer() + {} - /** - * Get the underlying value. - * - * This method will dereference the pointer and return the - * variable's value. - */ - virtual T getValue () const { return *_ptr; } + /** + * Get the underlying value. + * + * This method will dereference the pointer and return the + * variable's value. + */ + virtual T getValue() const + { + return *_ptr; + } - /** - * Set the underlying value. - * - * This method will dereference the pointer and change the - * variable's value. - */ - virtual bool setValue (T value) { *_ptr = value; return true; } + /** + * Set the underlying value. + * + * This method will dereference the pointer and change the + * variable's value. + */ + virtual bool setValue(T value) + { + *_ptr = value; + return true; + } - /** - * Create a copy of this raw value. - * - * The copy will use the same external pointer as the original. - */ - virtual SGRaw* clone () const { - return new SGRawValuePointer(_ptr); - } + /** + * Create a copy of this raw value. + * + * The copy will use the same external pointer as the original. + */ + virtual SGRaw* clone() const + { + return new SGRawValuePointer(_ptr); + } private: - T * _ptr; + T* _ptr; }; @@ -442,68 +449,76 @@ class SGRawValueFunctions : public SGRawValue { public: - /** - * The template type of a static getter function. - */ - typedef T (*getter_t)(); + /** + * The template type of a static getter function. + */ + typedef T (*getter_t)(); - /** - * The template type of a static setter function. - */ - typedef void (*setter_t)(T); + /** + * The template type of a static setter function. + */ + typedef void (*setter_t)(T); - /** - * Explicit constructor. - * - * Create a new raw value bound to the getter and setter supplied. - * - * @param getter A static function for getting a value, or 0 - * to read-disable the value. - * @param setter A static function for setting a value, or 0 - * to write-disable the value. - */ - SGRawValueFunctions (getter_t getter = 0, setter_t setter = 0) - : _getter(getter), _setter(setter) {} + /** + * Explicit constructor. + * + * Create a new raw value bound to the getter and setter supplied. + * + * @param getter A static function for getting a value, or 0 + * to read-disable the value. + * @param setter A static function for setting a value, or 0 + * to write-disable the value. + */ + SGRawValueFunctions(getter_t getter = 0, setter_t setter = 0) + : _getter(getter), _setter(setter) + {} - /** - * Destructor. - */ - virtual ~SGRawValueFunctions () {} + /** + * Destructor. + */ + virtual ~SGRawValueFunctions() + {} - /** - * Get the underlying value. - * - * This method will invoke the getter function to get a value. - * If no getter function was supplied, this method will always - * return the default value for the type. - */ - virtual T getValue () const { - if (_getter) return (*_getter)(); - else return SGRawValue::DefaultValue(); - } + /** + * Get the underlying value. + * + * This method will invoke the getter function to get a value. + * If no getter function was supplied, this method will always + * return the default value for the type. + */ + virtual T getValue() const + { + if (_getter) return (*_getter)(); + else return SGRawValue::DefaultValue(); + } - /** - * Set the underlying value. - * - * This method will invoke the setter function to change the - * underlying value. If no setter function was supplied, this - * method will return false. - */ - virtual bool setValue (T value) { - if (_setter) { (*_setter)(value); return true; } - else return false; - } + /** + * Set the underlying value. + * + * This method will invoke the setter function to change the + * underlying value. If no setter function was supplied, this + * method will return false. + */ + virtual bool setValue(T value) + { + if (_setter) { + (*_setter)(value); + return true; + } + else return false; + } - /** - * Create a copy of this raw value, bound to the same functions. - */ - virtual SGRaw* clone () const { - return new SGRawValueFunctions(_getter,_setter); - } + /** + * Create a copy of this raw value, bound to the same functions. + */ + virtual SGRaw* clone () const + { + return new SGRawValueFunctions(_getter,_setter); + } private: - getter_t _getter; - setter_t _setter; + getter_t _getter; + setter_t _setter; }; @@ -521,26 +536,40 @@ template class SGRawValueFunctionsIndexed : public SGRawValue { public: - typedef T (*getter_t)(int); - typedef void (*setter_t)(int,T); - SGRawValueFunctionsIndexed (int index, getter_t getter = 0, setter_t setter = 0) - : _index(index), _getter(getter), _setter(setter) {} - virtual ~SGRawValueFunctionsIndexed () {} - virtual T getValue () const { - if (_getter) return (*_getter)(_index); - else return SGRawValue::DefaultValue(); - } - virtual bool setValue (T value) { - if (_setter) { (*_setter)(_index, value); return true; } - else return false; - } - virtual SGRaw* clone () const { - return new SGRawValueFunctionsIndexed(_index, _getter, _setter); - } + typedef T (*getter_t)(int); + typedef void (*setter_t)(int,T); + + SGRawValueFunctionsIndexed(int index, getter_t getter = 0, setter_t setter = 0) + : _index(index), _getter(getter), _setter(setter) + {} + + virtual ~SGRawValueFunctionsIndexed() + {} + + virtual T getValue() const + { + if (_getter) return (*_getter)(_index); + else return SGRawValue::DefaultValue(); + } + + virtual bool setValue(T value) + { + if (_setter) { + (*_setter)(_index, value); + return true; + } + else return false; + } + + virtual SGRaw* clone() const + { + return new SGRawValueFunctionsIndexed(_index, _getter, _setter); + } + private: - int _index; - getter_t _getter; - setter_t _setter; + int _index; + getter_t _getter; + setter_t _setter; }; @@ -554,26 +583,45 @@ template class SGRawValueMethods : public SGRawValue { public: - typedef T (C::*getter_t)() const; - typedef void (C::*setter_t)(T); - SGRawValueMethods (C &obj, getter_t getter = 0, setter_t setter = 0) - : _obj(obj), _getter(getter), _setter(setter) {} - virtual ~SGRawValueMethods () {} - virtual T getValue () const { - if (_getter) { return (_obj.*_getter)(); } - else { return SGRawValue::DefaultValue(); } - } - virtual bool setValue (T value) { - if (_setter) { (_obj.*_setter)(value); return true; } - else return false; - } - virtual SGRaw* clone () const { - return new SGRawValueMethods(_obj, _getter, _setter); - } + typedef T (C::*getter_t)() const; + typedef void (C::*setter_t)(T); + + SGRawValueMethods(C &obj, getter_t getter = 0, setter_t setter = 0) + : _obj(obj), _getter(getter), _setter(setter) + {} + + virtual ~SGRawValueMethods() + {} + + virtual T getValue() const + { + if (_getter) { + return (_obj.*_getter)(); + } + else { + return SGRawValue::DefaultValue(); + } + } + + virtual bool setValue(T value) + { + if (_setter) + { + (_obj.*_setter)(value); + return true; + } + else return false; + } + + virtual SGRaw* clone() const + { + return new SGRawValueMethods(_obj, _getter, _setter); + } + private: - C &_obj; - getter_t _getter; - setter_t _setter; + C &_obj; + getter_t _getter; + setter_t _setter; }; @@ -590,29 +638,47 @@ template class SGStringValueMethods : public SGRawValue { public: - typedef std::string (C::*getter_t)() const; - typedef void (C::*setter_t)(const std::string&); - SGStringValueMethods (C &obj, getter_t getter = 0, setter_t setter = 0) - : _obj(obj), _getter(getter), _setter(setter) {} - virtual ~SGStringValueMethods () {} - virtual const char* getValue () const { - SGStringValueMethods* pthis = const_cast(this); - if (_getter) { pthis->_value = (_obj.*_getter)(); - return pthis->_value.c_str(); } - else { return SGRawValue::DefaultValue(); } - } - virtual bool setValue (const char* value) { - if (_setter) { (_obj.*_setter)(value ? value : ""); return true; } - else return false; - } - virtual SGRaw* clone () const { - return new SGStringValueMethods(_obj, _getter, _setter); - } + typedef std::string (C::*getter_t)() const; + typedef void (C::*setter_t)(const std::string&); + + SGStringValueMethods (C &obj, getter_t getter = 0, setter_t setter = 0) + : _obj(obj), _getter(getter), _setter(setter) + {} + + virtual ~SGStringValueMethods() + {} + + virtual const char* getValue() const + { + SGStringValueMethods* pthis = const_cast(this); + if (_getter) { + pthis->_value = (_obj.*_getter)(); + return pthis->_value.c_str(); + } + else { + return SGRawValue::DefaultValue(); + } + } + + virtual bool setValue(const char* value) + { + if (_setter) { + (_obj.*_setter)(value ? value : ""); + return true; + } + else return false; + } + + virtual SGRaw* clone() const + { + return new SGStringValueMethods(_obj, _getter, _setter); + } + private: - C &_obj; - getter_t _getter; - setter_t _setter; - std::string _value; + C &_obj; + getter_t _getter; + setter_t _setter; + std::string _value; }; @@ -626,28 +692,44 @@ template class SGRawValueMethodsIndexed : public SGRawValue { public: - typedef T (C::*getter_t)(int) const; - typedef void (C::*setter_t)(int, T); - SGRawValueMethodsIndexed (C &obj, int index, - getter_t getter = 0, setter_t setter = 0) - : _obj(obj), _index(index), _getter(getter), _setter(setter) {} - virtual ~SGRawValueMethodsIndexed () {} - virtual T getValue () const { - if (_getter) { return (_obj.*_getter)(_index); } - else { return SGRawValue::DefaultValue(); } - } - virtual bool setValue (T value) { - if (_setter) { (_obj.*_setter)(_index, value); return true; } - else return false; - } - virtual SGRaw* clone () const { - return new SGRawValueMethodsIndexed(_obj, _index, _getter, _setter); - } + typedef T (C::*getter_t)(int) const; + typedef void (C::*setter_t)(int, T); + SGRawValueMethodsIndexed(C &obj, int index, getter_t getter = 0, setter_t setter = 0) + : _obj(obj), _index(index), _getter(getter), _setter(setter) + {} + + virtual ~SGRawValueMethodsIndexed() + {} + + virtual T getValue() const + { + if (_getter) + { + return (_obj.*_getter)(_index); + } + else { + return SGRawValue::DefaultValue(); + } + } + virtual bool setValue(T value) + { + if (_setter) { + (_obj.*_setter)(_index, value); + return true; + } + else return false; + } + + virtual SGRaw* clone() const + { + return new SGRawValueMethodsIndexed(_obj, _index, _getter, _setter); + } + private: - C &_obj; - int _index; - getter_t _getter; - setter_t _setter; + C &_obj; + int _index; + getter_t _getter; + setter_t _setter; }; /** @@ -663,17 +745,22 @@ public: /** * Explicit constructor. */ - SGRawValueContainer(const T& obj) : _obj(obj) {} + SGRawValueContainer(const T& obj) : _obj(obj) + {} /** * Destructor. */ - virtual ~SGRawValueContainer() {} + virtual ~SGRawValueContainer() + {} /** * Get the underlying value. */ - virtual T getValue() const { return _obj; } + virtual T getValue() const + { + return _obj; + } /** * Set the underlying value. @@ -681,12 +768,17 @@ public: * This method will dereference the pointer and change the * variable's value. */ - virtual bool setValue (T value) { _obj = value; return true; } + virtual bool setValue(T value) + { + _obj = value; + return true; + } /** * Create a copy of this raw value. */ - virtual SGRaw* clone () const { + virtual SGRaw* clone() const + { return new SGRawValueContainer(_obj); } @@ -697,8 +789,9 @@ private: template SGRawExtended* SGRawBase::makeContainer() const { - return new SGRawValueContainer(static_cast*>(this) - ->getValue()); + return new SGRawValueContainer( + static_cast*>(this)->getValue() + ); } template @@ -737,30 +830,37 @@ namespace simgear class SGPropertyChangeListener { public: - virtual ~SGPropertyChangeListener (); + virtual ~SGPropertyChangeListener(); - /// Called if value of \a node has changed. - virtual void valueChanged(SGPropertyNode * node); + /// Called if value of \a node has changed. + virtual void valueChanged(SGPropertyNode* node); - /// Called if \a child has been added to the given \a parent. - virtual void childAdded(SGPropertyNode * parent, SGPropertyNode * child); + /// Called if \a child has been added to the given \a parent. + virtual void childAdded(SGPropertyNode* parent, SGPropertyNode* child); - /// Called if \a child has been removed from its \a parent. - virtual void childRemoved(SGPropertyNode * parent, SGPropertyNode * child); + /// Called if \a child has been removed from its \a parent. + virtual void childRemoved(SGPropertyNode* parent, SGPropertyNode* child); protected: SGPropertyChangeListener(bool recursive = false); - friend class SGPropertyNode; - virtual void register_property (SGPropertyNode * node); - virtual void unregister_property (SGPropertyNode * node); + friend class SGPropertyNode; + virtual void register_property(SGPropertyNode* node); + virtual void unregister_property(SGPropertyNode* node); private: - std::vector _properties; + std::vector _properties; }; struct SGPropertyNodeListeners; +/* Forward declarations for internal locking implementation. */ +struct SGPropertyLock; +struct SGPropertyLockShared; +struct SGPropertyLockExclusive; + +/* Forward declaration for implementation details. */ +struct SGPropertyNodeImpl; /** * A node in a property tree. @@ -769,1111 +869,558 @@ class SGPropertyNode : public SGReferenced { public: - /** - * Public constants. - */ - enum { - MAX_STRING_LEN = 1024 - }; - - /** - * Access mode attributes. - * - *

The ARCHIVE attribute is strictly advisory, and controls - * whether the property should normally be saved and restored.

- */ - enum Attribute { - NO_ATTR = 0, - READ = 1, - WRITE = 2, - ARCHIVE = 4, - REMOVED = 8, - TRACE_READ = 16, - TRACE_WRITE = 32, - USERARCHIVE = 64, - PRESERVE = 128, - PROTECTED = 1 << 8, - LISTENER_SAFE = 1 << 9, /// it's safe to listen to this property, even if it's tied - // beware: if you add another attribute here, - // also update value of "LAST_USED_ATTRIBUTE". - }; - - - /** - * Last used attribute - * Update as needed when enum Attribute is changed - */ - static const int LAST_USED_ATTRIBUTE; - - /** - * Default constructor. - */ - SGPropertyNode (); - - - /** - * Copy constructor. - */ - SGPropertyNode (const SGPropertyNode &node); - - - /** - * Destructor. - */ - virtual ~SGPropertyNode (); - - - - // - // Basic properties. - // - - /** - * Test whether this node contains a primitive leaf value. - */ - bool hasValue () const { return (_type != simgear::props::NONE); } - - - /** - * Get the node's simple (XML) name. - */ - const char * getName () const { return _name.c_str(); } - - /** - * Get the node's simple name as a string. - */ - const std::string& getNameString () const { return _name; } - - /** - * Get the node's pretty display name, with subscript when needed. - */ - std::string getDisplayName (bool simplify = false) const; - - - /** - * Get the node's integer index. - */ - int getIndex () const { return _index; } - - - /** - * Get a non-const pointer to the node's parent. - */ - SGPropertyNode * getParent () { return _parent; } - - - /** - * Get a const pointer to the node's parent. - */ - const SGPropertyNode * getParent () const { return _parent; } - /** - * Get node's position in parent (*NOT* index) + * Access mode attributes. + * + *

The ARCHIVE attribute is strictly advisory, and controls + * whether the property should normally be saved and restored.

*/ + enum Attribute { + NO_ATTR = 0, + READ = 1, + WRITE = 2, + ARCHIVE = 4, + REMOVED = 8, + TRACE_READ = 16, + TRACE_WRITE = 32, + USERARCHIVE = 64, + PRESERVE = 128, + PROTECTED = 1 << 8, + LISTENER_SAFE = 1 << 9, /// it's safe to listen to this property, even if it's tied + // beware: if you add another attribute here, + // also update value of "LAST_USED_ATTRIBUTE". + }; + + /** Last used attribute. */ + static const int LAST_USED_ATTRIBUTE; + + /** Default constructor. */ + SGPropertyNode(); + + /** Copy constructor. */ + SGPropertyNode(const SGPropertyNode &node); + + /** Destructor. */ + virtual ~SGPropertyNode(); + + // + // Basic properties. + // + + /** Test whether this node contains a primitive leaf value. */ + bool hasValue() const; + + /** Get the node's simple (XML) name. Return value is not thread-safe. */ + const char* getName() const; + + /** Get the node's simple name as a string. Return value is not thread-safe. */ + const std::string& getNameString() const; + + /** Get the node's pretty display name, with subscript when needed. */ + std::string getDisplayName(bool simplify = false) const; + + /** Get the node's integer index. */ + int getIndex() const; + + /** Get node's position (contiguous values) in parent (*NOT* index) */ unsigned int getPosition() const; - // - // Children. - // + /** Get a pointer to the node's parent. */ + SGPropertyNode* getParent(); + const SGPropertyNode* getParent() const; + // + // Children. + // - /** - * Get the number of child nodes. - */ - int nChildren () const { return (int)_children.size(); } + /** Get the number of child nodes. */ + int nChildren() const; + /** Get a child node by position (*NOT* index). */ + SGPropertyNode* getChild(int position); + const SGPropertyNode* getChild(int position) const; - /** - * Get a child node by position (*NOT* index). - */ - SGPropertyNode * getChild (int position); - - - /** - * Get a const child node by position (*NOT* index). - */ - const SGPropertyNode * getChild (int position) const; - - - /** - * Test whether a named child exists. - */ - bool hasChild (const char * name, int index = 0) const - { - return (getChild(name, index) != 0); - } - - /** - * Test whether a named child exists. - */ - bool hasChild (const std::string& name, int index = 0) const - { - return (getChild(name, index) != 0); - } - - /** - * Create a new child node with the given name and an unused index - * - * @param min_index Minimal index for new node (skips lower indices) - * @param append Whether to simply use the index after the last used index - * or use a lower, unused index if it exists - */ - SGPropertyNode * addChild ( const char* name, - int min_index = 0, - bool append = true ); - SGPropertyNode * addChild ( const std::string& name, - int min_index = 0, - bool append = true ) - { return addChild(name.c_str(), min_index, append); } - - /** - * Add existing node as child. - * - * @param min_index Minimal index for new node (skips lower indices) - * @param append Whether to simply use the index after the last used index - * or use a lower, unused index if it exists - */ - SGPropertyNode_ptr addChild(SGPropertyNode_ptr node, const std::string& name, - int min_index=0, bool append=true); - - /** - * Create multiple child nodes with the given name an unused indices - * - * @param count The number of nodes create - * @param min_index Minimal index for new nodes (skips lower indices) - * @param append Whether to simply use the index after the last used index - * or use a lower, unused index if it exists - */ - simgear::PropertyList addChildren ( const std::string& name, - size_t count, - int min_index = 0, - bool append = true ); - - /** - * Get a child node by name and index. - */ - SGPropertyNode * getChild (const char* name, int index = 0, - bool create = false); - SGPropertyNode * getChild (const std::string& name, int index = 0, - bool create = false); - /** - * Get a const child node by name and index. - */ - const SGPropertyNode * getChild (const char * name, int index = 0) const; - - /** - * Get a const child node by name and index. - */ - const SGPropertyNode * getChild (const std::string& name, int index = 0) const - { return getChild(name.c_str(), index); } - - - /** - * Get a vector of all children with the specified name. - */ - simgear::PropertyList getChildren (const char * name) const; - - /** - * Get a vector of all children with the specified name. - */ - simgear::PropertyList getChildren (const std::string& name) const - { return getChildren(name.c_str()); } - - /** - * Remove child by pointer (if it is a child of this node). - * - * @return true, if the node was deleted. - */ - bool removeChild(SGPropertyNode* node); - - // TODO do we need the removeXXX methods to return the deleted nodes? - /** - * Remove child by position. - */ - SGPropertyNode_ptr removeChild(int pos); - - - /** - * Remove a child node - */ - SGPropertyNode_ptr removeChild(const char * name, int index = 0); - - /** - * Remove a child node - */ - SGPropertyNode_ptr removeChild(const std::string& name, int index = 0) - { return removeChild(name.c_str(), index); } - - /** - * Remove all children with the specified name. - */ - simgear::PropertyList removeChildren(const char * name); - - /** - * Remove all children with the specified name. - */ - simgear::PropertyList removeChildren(const std::string& name) - { return removeChildren(name.c_str()); } - - /** - * Remove all children (does not change the value of the node) - */ - void removeAllChildren(); - - // - // Alias support. - // - - - /** - * Alias this node's leaf value to another's. - */ - bool alias (SGPropertyNode * target); - - - /** - * Alias this node's leaf value to another's by relative path. - */ - bool alias (const char * path); - - /** - * Alias this node's leaf value to another's by relative path. - */ - bool alias (const std::string& path) - { return alias(path.c_str()); } - - - /** - * Remove any alias for this node. - */ - bool unalias (); - - - /** - * Test whether the node's leaf value is aliased to another's. - */ - bool isAlias () const { return (_type == simgear::props::ALIAS); } - - - /** - * Get a non-const pointer to the current alias target, if any. - */ - SGPropertyNode * getAliasTarget (); - - - /** - * Get a const pointer to the current alias target, if any. - */ - const SGPropertyNode * getAliasTarget () const; - - - // - // Path information. - // - - - /** - * Get the path to this node from the root. - */ - std::string getPath (bool simplify = false) const; - - - /** - * Get a pointer to the root node. - */ - SGPropertyNode * getRootNode (); - - - /** - * Get a const pointer to the root node. - */ - const SGPropertyNode * getRootNode () const; - - - /** - * Get a pointer to another node by relative path. - */ - SGPropertyNode * getNode (const char * relative_path, bool create = false); - - /** - * deep copy one node to another. - */ - void copy(SGPropertyNode *to) const; - - /** - * Get a pointer to another node by relative path. - */ - SGPropertyNode * getNode (const std::string& relative_path, bool create = false) - { return getNode(relative_path.c_str(), create); } - - /** - * Get a pointer to another node by relative path. - * - * This method leaves the index off the last member of the path, - * so that the user can specify it separately (and save some - * string building). For example, getNode("/bar[1]/foo", 3) is - * exactly equivalent to getNode("bar[1]/foo[3]"). The index - * provided overrides any given in the path itself for the last - * component. - */ - SGPropertyNode * getNode (const char * relative_path, int index, - bool create = false); - - /** - * Get a pointer to another node by relative path. - * - * This method leaves the index off the last member of the path, - * so that the user can specify it separately (and save some - * string building). For example, getNode("/bar[1]/foo", 3) is - * exactly equivalent to getNode("bar[1]/foo[3]"). The index - * provided overrides any given in the path itself for the last - * component. - */ - SGPropertyNode * getNode (const std::string& relative_path, int index, - bool create = false) - { return getNode(relative_path.c_str(), index, create); } - - /** - * Get a const pointer to another node by relative path. - */ - const SGPropertyNode * getNode (const char * relative_path) const; - - /** - * Get a const pointer to another node by relative path. - */ - const SGPropertyNode * getNode (const std::string& relative_path) const - { return getNode(relative_path.c_str()); } - - - /** - * Get a const pointer to another node by relative path. - * - * This method leaves the index off the last member of the path, - * so that the user can specify it separate. - */ - const SGPropertyNode * getNode (const char * relative_path, - int index) const; - - /** - * Get a const pointer to another node by relative path. - * - * This method leaves the index off the last member of the path, - * so that the user can specify it separate. - */ - const SGPropertyNode * getNode (const std::string& relative_path, - int index) const - { return getNode(relative_path.c_str(), index); } - - // - // Access Mode. - // - - /** - * Check a single mode attribute for the property node. - */ - bool getAttribute (Attribute attr) const { return ((_attr & attr) != 0); } - - - /** - * Set a single mode attribute for the property node. - */ - void setAttribute (Attribute attr, bool state) { - (state ? _attr |= attr : _attr &= ~attr); - } - - - /** - * Get all of the mode attributes for the property node. - */ - int getAttributes () const { return _attr; } - - - /** - * Set all of the mode attributes for the property node. - */ - void setAttributes (int attr) { _attr = attr; } - - - // - // Leaf Value (primitive). - // - - - /** - * Get the type of leaf value, if any, for this node. - */ - simgear::props::Type getType () const; - - - /** - * Get a bool value for this node. - */ - bool getBoolValue () const; - - - /** - * Get an int value for this node. - */ - int getIntValue () const; - - - /** - * Get a long int value for this node. - */ - long getLongValue () const; - - - /** - * Get a float value for this node. - */ - float getFloatValue () const; - - - /** - * Get a double value for this node. - */ - double getDoubleValue () const; - - - /** - * Get a string value for this node. - */ - 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 std::enable_if::Internal> - ::type* dummy = 0) const; - // Getter for extended property - template - T getValue(typename std::enable_if::Internal> - ::type* dummy = 0) const; - - /** - * Get a list of values from all children with the given name - */ - template // TODO use C++11 or traits - std::vector getChildValues(const std::string& name) const; - - /** - * Get a list of values from all children with the given name - */ - template - std::vector getChildValues(const std::string& name) const; - - /** - * Set a bool value for this node. - */ - bool setBoolValue (bool value); - - - /** - * Set an int value for this node. - */ - bool setIntValue (int value); - - - /** - * Set a long int value for this node. - */ - bool setLongValue (long value); - - - /** - * Set a float value for this node. - */ - bool setFloatValue (float value); - - - /** - * Set a double value for this node. - */ - bool setDoubleValue (double value); - - - /** - * Set a string value for this node. - */ - bool setStringValue (const char * value); - - /** - * Set a string value for this node. - */ - bool setStringValue (const std::string& value) - { return setStringValue(value.c_str()); } - - - /** - * Set a value of unspecified type for this node. - */ - bool setUnspecifiedValue (const char * value); - - template - bool setValue(const T& val, - typename std::enable_if::Internal> - ::type* dummy = 0); - - template - bool setValue(const T& val, - typename std::enable_if::Internal> - ::type* dummy = 0); - - template - bool setValue(const char (&val)[N]) - { - return setValue(&val[0]); - } - - /** - * Set relative node to given value and afterwards make read only. - * - * @param relative_path Path to node - * @param value Value to set - * - * @return whether value could be set - */ - template - bool setValueReadOnly(const std::string& relative_path, const T& value) - { - SGPropertyNode* node = getNode(relative_path, true); - bool ret = node->setValue(value); - node->setAttributes(READ); - return ret; - } - -#if !PROPS_STANDALONE - /** - * Interpolate current value to target value within given time. - * - * @param type Type of interpolation ("numeric", "color", etc.) - * @param target Node containing target value - * @param duration Duration of interpolation (in seconds) - * @param easing Easing function (http://easings.net/) - */ - bool interpolate( const std::string& type, - const SGPropertyNode& target, - double duration = 0.6, - const std::string& easing = "swing" ); - - /** - * Interpolate current value to a series of values within given durations. - * - * @param type Type of interpolation ("numeric", "color", etc.) - * @param values Nodes containing intermediate and target values - * @param deltas Durations for each interpolation step (in seconds) - * @param easing Easing function (http://easings.net/) - */ - bool interpolate( const std::string& type, - const simgear::PropertyList& values, - const double_list& deltas, - const std::string& easing = "swing" ); - - /** - * Set the interpolation manager used by the interpolate methods. - */ - static void setInterpolationMgr(simgear::PropertyInterpolationMgr* mgr); - - /** - * Get the interpolation manager - */ - static simgear::PropertyInterpolationMgr* getInterpolationMgr(); -#endif - - /** - * Print the value of the property to a stream. - */ - std::ostream& printOn(std::ostream& stream) const; - - // - // Data binding. - // - - - /** - * Test whether this node is bound to an external data source. - */ - bool isTied () const { return _tied; } + /** Test whether a named child exists. */ + bool hasChild(const char* name, int index = 0) const; + bool hasChild(const std::string& name, int index = 0) const; /** - * Bind this node to an external source. + * Create a new child node with the given name and an unused index + * + * @param min_index Minimal index for new node (skips lower indices) + * @param append Whether to simply use the index after the last used index + * or use a lower, unused index if it exists */ + SGPropertyNode* addChild( const char* name, int min_index = 0, bool append = true ); + SGPropertyNode* addChild( const std::string& name, int min_index = 0, bool append = true ); + + /** + * Add existing node as child. + * + * @param min_index Minimal index for new node (skips lower indices) + * @param append Whether to simply use the index after the last used index + * or use a lower, unused index if it exists + */ + SGPropertyNode_ptr addChild(SGPropertyNode_ptr node, const std::string& name, int min_index=0, bool append=true); + + /** + * Create multiple child nodes with the given name an unused indices + * + * @param count The number of nodes create + * @param min_index Minimal index for new nodes (skips lower indices) + * @param append Whether to simply use the index after the last used index + * or use a lower, unused index if it exists + */ + simgear::PropertyList addChildren( const std::string& name, size_t count, int min_index = 0, bool append = true ); + + /** Get a child node by name and index. */ + SGPropertyNode* getChild(const char* name, int index = 0, bool create = false); + SGPropertyNode* getChild(const std::string& name, int index = 0, bool create = false); + const SGPropertyNode* getChild(const char* name, int index = 0) const; + const SGPropertyNode* getChild(const std::string& name, int index = 0) const; + + /** Get a vector of all children with the specified name. */ + simgear::PropertyList getChildren(const char* name) const; + simgear::PropertyList getChildren(const std::string& name) const; + + /** If is child of this node, remove it and return true. Otherwise return false. */ + bool removeChild(SGPropertyNode* node); + + /** Remove child by position and name. */ + SGPropertyNode_ptr removeChild(int pos); + SGPropertyNode_ptr removeChild(const char* name, int index = 0); + SGPropertyNode_ptr removeChild(const std::string& name, int index = 0); + + /** Remove all children with the specified name. */ + simgear::PropertyList removeChildren(const char* name); + simgear::PropertyList removeChildren(const std::string& name); + + /** Remove all children (does not change the value of the node) */ + void removeAllChildren(); + + // + // Alias support. + // + + /** Alias this node's leaf value to another's. */ + bool alias(SGPropertyNode* target); + + /** Alias this node's leaf value to another's by relative path. */ + bool alias(const char* path); + bool alias(const std::string& path); + + /** Remove any alias for this node. */ + bool unalias(); + + /** Test whether the node's leaf value is aliased to another's. */ + bool isAlias() const; + + /** Get a pointer to the current alias target, if any. */ + SGPropertyNode* getAliasTarget(); + const SGPropertyNode* getAliasTarget() const; + + // + // Path information. + // + + /** Get the path to this node from the root. */ + std::string getPath(bool simplify = false) const; + + /** Get a pointer to the root node. */ + SGPropertyNode* getRootNode(); + const SGPropertyNode* getRootNode() const; + + /** deep copy one node to another. */ + void copy(SGPropertyNode*to) const; + + /** Get a pointer to another node by relative path. */ + SGPropertyNode* getNode(const char* relative_path, bool create = false); + SGPropertyNode* getNode(const std::string& relative_path, bool create = false); + const SGPropertyNode* getNode(const char* relative_path) const; + const SGPropertyNode* getNode(const std::string& relative_path) const; + + /** + * Get a pointer to another node by relative path. + * + * This method leaves the index off the last member of the path, + * so that the user can specify it separately (and save some + * string building). For example, getNode("/bar[1]/foo", 3) is + * exactly equivalent to getNode("bar[1]/foo[3]"). The index + * provided overrides any given in the path itself for the last + * component. + */ + SGPropertyNode* getNode(const char* relative_path, int index, bool create = false); + SGPropertyNode* getNode(const std::string& relative_path, int index, bool create = false); + const SGPropertyNode* getNode(const char* relative_path, int index) const; + const SGPropertyNode* getNode(const std::string& relative_path, int index) const; + + // + // Access Mode. + // + + /** Get or set a single mode attribute for the property node. */ + bool getAttribute(Attribute attr) const; + void setAttribute(Attribute attr, bool state); + + /** Get or set all of the mode attributes for the property node. */ + int getAttributes() const; + void setAttributes(int attr); + + // + // Leaf Value (primitive). + // + + /** Get the type of leaf value, if any, for this node. */ + simgear::props::Type getType() const; + + /** Get a value of this node. */ + bool getBoolValue() const; + int getIntValue() const; + long getLongValue() const; + float getFloatValue() const; + double getDoubleValue() const; + const char* getStringValue() const; + + /** Set value of this node. */ + bool setBoolValue(bool value); + bool setIntValue(int value); + bool setLongValue(long value); + bool setFloatValue(float value); + bool setDoubleValue(double value); + bool setStringValue(const char* value); + bool setStringValue(const std::string& value); + + /** Set a value of unspecified type for this node. */ + bool setUnspecifiedValue(const char* value); + + // + // Template methods for get/set value. + // + + /** + * 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 std::enable_if::Internal>::type* dummy = 0) const; + + // Getter for extended property + template + T getValue(typename std::enable_if::Internal>::type* dummy = 0) const; + + /** Get a list of values from all children with the given name. */ + template // TODO use C++11 or traits + std::vector getChildValues(const std::string& name) const; + + /** Get a list of values from all children with the given name. */ + template + std::vector getChildValues(const std::string& name) const; + + template + bool setValue(const T& val, + typename std::enable_if::Internal>::type* dummy = 0); + + template + bool setValue(const T& val, + typename std::enable_if::Internal>::type* dummy = 0); + + template + bool setValue(const char(&val)[N]) + { + return setValue(&val[0]); + } + + template + bool setValue( + SGPropertyLockExclusive& exclusive, + const T& val, + typename std::enable_if::Internal>::type* dummy = 0 + ); + + template + bool setValue( + SGPropertyLockExclusive& exclusive, + const T& val, + typename std::enable_if::Internal>::type* dummy = 0 + ); + + /** + * Set relative node to given value and afterwards make read only. + * + * @param relative_path Path to node + * @param value Value to set + * + * @return whether value could be set + */ + template + bool setValueReadOnly(const std::string& relative_path, const T& value) + { + SGPropertyNode* node = getNode(relative_path, true); + bool ret = node->setValue(value); + node->setAttributes(READ); + return ret; + } + +#if !PROPS_STANDALONE + /** + * Interpolate current value to target value within given time. + * + * @param type Type of interpolation ("numeric", "color", etc.) + * @param target Node containing target value + * @param duration Duration of interpolation (in seconds) + * @param easing Easing function (http://easings.net/) + */ + bool interpolate( const std::string& type, + const SGPropertyNode& target, + double duration = 0.6, + const std::string& easing = "swing" ); + + /** + * Interpolate current value to a series of values within given durations. + * + * @param type Type of interpolation ("numeric", "color", etc.) + * @param values Nodes containing intermediate and target values + * @param deltas Durations for each interpolation step (in seconds) + * @param easing Easing function (http://easings.net/) + */ + bool interpolate( const std::string& type, + const simgear::PropertyList& values, + const double_list& deltas, + const std::string& easing = "swing" ); + + /** Set the interpolation manager used by the interpolate methods. */ + static void setInterpolationMgr(simgear::PropertyInterpolationMgr* mgr); + + /** Get the interpolation manager. */ + static simgear::PropertyInterpolationMgr* getInterpolationMgr(); +#endif + + /** Print the value of the property to a stream. */ + std::ostream& printOn(std::ostream& stream) const; + + // + // Data binding. + // + + /** Test whether this node is bound to an external data source. */ + bool isTied() const; + + /** Bind this node to an external source. */ template bool tie(const SGRawValue &rawValue, bool useDefault = true); - /** - * Unbind this node from any external data source. - */ - bool untie (); - - - // - // Convenience methods using paths. - // TODO: add attribute methods - // - - - /** - * Get another node's type. - */ - simgear::props::Type getType (const char * relative_path) const; - - /** - * Get another node's type. - */ - simgear::props::Type getType (const std::string& relative_path) const - { return getType(relative_path.c_str()); } - - /** - * Test whether another node has a leaf value. - */ - bool hasValue (const char * relative_path) const; - - /** - * Test whether another node has a leaf value. - */ - bool hasValue (const std::string& relative_path) const - { return hasValue(relative_path.c_str()); } - - /** - * Get another node's value as a bool. - */ - bool getBoolValue (const char * relative_path, - bool defaultValue = false) const; - - /** - * Get another node's value as a bool. - */ - bool getBoolValue (const std::string& relative_path, - bool defaultValue = false) const - { return getBoolValue(relative_path.c_str(), defaultValue); } - - /** - * Get another node's value as an int. - */ - int getIntValue (const char * relative_path, - int defaultValue = 0) const; - - /** - * Get another node's value as an int. - */ - int getIntValue (const std::string& relative_path, - int defaultValue = 0) const - { return getIntValue(relative_path.c_str(), defaultValue); } - - - /** - * Get another node's value as a long int. - */ - long getLongValue (const char * relative_path, - long defaultValue = 0L) const; - - /** - * Get another node's value as a long int. - */ - long getLongValue (const std::string& relative_path, - long defaultValue = 0L) const - { return getLongValue(relative_path.c_str(), defaultValue); } - - /** - * Get another node's value as a float. - */ - float getFloatValue (const char * relative_path, - float defaultValue = 0.0f) const; - - /** - * Get another node's value as a float. - */ - float getFloatValue (const std::string& relative_path, - float defaultValue = 0.0f) const - { return getFloatValue(relative_path.c_str(), defaultValue); } - - - /** - * Get another node's value as a double. - */ - double getDoubleValue (const char * relative_path, - double defaultValue = 0.0) const; - - /** - * Get another node's value as a double. - */ - double getDoubleValue (const std::string& relative_path, - double defaultValue = 0.0) const - { return getDoubleValue(relative_path.c_str(), defaultValue); } - - /** - * Get another node's value as a string. - */ - const char * getStringValue (const char * relative_path, - const char * defaultValue = "") const; - - - /** - * Get another node's value as a string. - */ - const char * getStringValue (const std::string& relative_path, - const char * defaultValue = "") const - { return getStringValue(relative_path.c_str(), defaultValue); } - - - /** - * Set another node's value as a bool. - */ - bool setBoolValue (const char * relative_path, bool value); - - /** - * Set another node's value as a bool. - */ - bool setBoolValue (const std::string& relative_path, bool value) - { return setBoolValue(relative_path.c_str(), value); } - - - /** - * Set another node's value as an int. - */ - bool setIntValue (const char * relative_path, int value); - - /** - * Set another node's value as an int. - */ - bool setIntValue (const std::string& relative_path, int value) - { return setIntValue(relative_path.c_str(), value); } - - - /** - * Set another node's value as a long int. - */ - bool setLongValue (const char * relative_path, long value); - - /** - * Set another node's value as a long int. - */ - bool setLongValue (const std::string& relative_path, long value) - { return setLongValue(relative_path.c_str(), value); } - - - /** - * Set another node's value as a float. - */ - bool setFloatValue (const char * relative_path, float value); - - /** - * Set another node's value as a float. - */ - bool setFloatValue (const std::string& relative_path, float value) - { return setFloatValue(relative_path.c_str(), value); } - - - /** - * Set another node's value as a double. - */ - bool setDoubleValue (const char * relative_path, double value); - - /** - * Set another node's value as a double. - */ - bool setDoubleValue (const std::string& relative_path, double value) - { return setDoubleValue(relative_path.c_str(), value); } - - - /** - * Set another node's value as a string. - */ - bool setStringValue (const char * relative_path, const char * value); - - bool setStringValue(const char * relative_path, const std::string& value) - { return setStringValue(relative_path, value.c_str()); } - /** - * Set another node's value as a string. - */ - bool setStringValue (const std::string& relative_path, const char * value) - { return setStringValue(relative_path.c_str(), value); } - - bool setStringValue (const std::string& relative_path, - const std::string& value) - { return setStringValue(relative_path.c_str(), value.c_str()); } - - /** - * Set another node's value with no specified type. - */ - bool setUnspecifiedValue (const char * relative_path, const char * value); - - - /** - * Test whether another node is bound to an external data source. - */ - bool isTied (const char * relative_path) const; - - /** - * Test whether another node is bound to an external data source. - */ - bool isTied (const std::string& relative_path) const - { return isTied(relative_path.c_str()); } - - /** - * Bind another node to an external bool source. - */ - bool tie (const char * relative_path, const SGRawValue &rawValue, - bool useDefault = true); - - /** - * Bind another node to an external bool source. - */ - bool tie (const std::string& relative_path, const SGRawValue &rawValue, - bool useDefault = true) - { return tie(relative_path.c_str(), rawValue, useDefault); } - - - /** - * Bind another node to an external int source. - */ - bool tie (const char * relative_path, const SGRawValue &rawValue, - bool useDefault = true); - - /** - * Bind another node to an external int source. - */ - bool tie (const std::string& relative_path, const SGRawValue &rawValue, - bool useDefault = true) - { return tie(relative_path.c_str(), rawValue, useDefault); } - - - /** - * Bind another node to an external long int source. - */ - bool tie (const char * relative_path, const SGRawValue &rawValue, - bool useDefault = true); - - /** - * Bind another node to an external long int source. - */ - bool tie (const std::string& relative_path, const SGRawValue &rawValue, - bool useDefault = true) - { return tie(relative_path.c_str(), rawValue, useDefault); } - - - /** - * Bind another node to an external float source. - */ - bool tie (const char * relative_path, const SGRawValue &rawValue, - bool useDefault = true); - - /** - * Bind another node to an external float source. - */ - bool tie (const std::string& relative_path, const SGRawValue &rawValue, - bool useDefault = true) - { return tie(relative_path.c_str(), rawValue, useDefault); } - - - /** - * Bind another node to an external double source. - */ - bool tie (const char * relative_path, const SGRawValue &rawValue, - bool useDefault = true); - - /** - * Bind another node to an external double source. - */ - bool tie (const std::string& relative_path, const SGRawValue &rawValue, - bool useDefault = true) - { return tie(relative_path.c_str(), rawValue, useDefault); } - - - /** - * Bind another node to an external string source. - */ - bool tie (const char * relative_path, const SGRawValue &rawValue, - bool useDefault = true); - - /** - * Bind another node to an external string source. - */ - bool tie (const std::string& relative_path, const SGRawValue &rawValue, - bool useDefault = true) - { return tie(relative_path.c_str(), rawValue, useDefault); } - - - /** - * Unbind another node from any external data source. - */ - bool untie (const char * relative_path); - - /** - * Unbind another node from any external data source. - */ - bool untie (const std::string& relative_path) - { return untie(relative_path.c_str()); } - - - /** - * Add a change listener to the property. If "initial" is set call the - * listener initially. - */ - void addChangeListener (SGPropertyChangeListener * listener, - bool initial = false); - - - /** - * Remove a change listener from the property. - */ - void removeChangeListener (SGPropertyChangeListener * listener); - - - /** - * Get the number of listeners. - */ - int nListeners () const; - - - /** - * Fire a value change event to all listeners. - */ - void fireValueChanged (); - - - /** - * Fire a child-added event to all listeners. - */ - void fireChildAdded (SGPropertyNode * child); - - /** - * Trigger a child-added and value-changed event for every child (Unlimited - * depth). - * - * @param fire_self Whether to trigger the events also for the node itself. - * - * It can be used to simulating the creation of a property tree, eg. for - * (re)initializing a subsystem which is controlled through the property tree. - */ - void fireCreatedRecursive(bool fire_self = false); - - /** - * Fire a child-removed event to all listeners. - */ - void fireChildRemoved (SGPropertyNode * child); - - /** - * Fire a child-removed event for every child of this node (Unlimited depth) - * - * Upon removal of a child node only for this single node a child-removed - * event is triggered. If eg. resource cleanup relies on receiving a - * child-removed event for every child this method can be used. - */ - void fireChildrenRemovedRecursive(); - - - /** - * Clear any existing value and set the type to NONE. - */ - void clearValue (); - - /** - * Compare two property trees. The property trees are equal if: 1) - * They have no children, and have the same type and the values are - * equal, or 2) have the same number of children, and the - * corresponding children in each tree are equal. "corresponding" - * means have the same name and index. - * - * Attributes, removed children, and aliases aren't considered. - */ - static bool compare (const SGPropertyNode& lhs, const SGPropertyNode& rhs); + /** Unbind this node from any external data source. */ + bool untie(); + + // + // Convenience methods using paths. + // TODO: add attribute methods + // + + /** Get another node's type. */ + simgear::props::Type getType(const char* relative_path) const; + simgear::props::Type getType(const std::string& relative_path) const; + + /** Test whether another node has a leaf value. */ + bool hasValue(const char* relative_path) const; + bool hasValue(const std::string& relative_path) const; + + /** Get another node's value. */ + bool getBoolValue(const char* relative_path, bool defaultValue = false) const; + int getIntValue(const char* relative_path, int defaultValue = 0) const; + long getLongValue(const char* relative_path, long defaultValue = 0L) const; + float getFloatValue(const char* relative_path, float defaultValue = 0.0f) const; + double getDoubleValue(const char* relative_path, double defaultValue = 0.0) const; + const char* getStringValue(const char* relative_path, const char* defaultValue = "") const; + + bool getBoolValue(const std::string& relative_path, bool defaultValue = false) const; + int getIntValue(const std::string& relative_path, int defaultValue = 0) const; + long getLongValue(const std::string& relative_path, long defaultValue = 0L) const; + float getFloatValue(const std::string& relative_path, float defaultValue = 0.0f) const; + double getDoubleValue(const std::string& relative_path, double defaultValue = 0.0) const; + const char* getStringValue(const std::string& relative_path, const char* defaultValue = "") const; + + /** Set another node's value. */ + bool setBoolValue(const char* relative_path, bool value); + bool setIntValue(const char* relative_path, int value); + bool setLongValue(const char* relative_path, long value); + bool setFloatValue(const char* relative_path, float value); + bool setDoubleValue(const char* relative_path, double value); + + bool setBoolValue(const std::string& relative_path, bool value); + bool setIntValue(const std::string& relative_path, int value); + bool setLongValue(const std::string& relative_path, long value); + bool setFloatValue(const std::string& relative_path, float value); + bool setDoubleValue(const std::string& relative_path, double value); + + bool setStringValue(const char* relative_path, const char* value); + bool setStringValue(const char* relative_path, const std::string& value); + + bool setStringValue(const std::string& relative_path, const char* value); + bool setStringValue(const std::string& relative_path, const std::string& value); + + /** Set another node's value with no specified type. */ + bool setUnspecifiedValue(const char* relative_path, const char* value); + + /** Test whether another node is bound to an external data source. */ + bool isTied(const char* relative_path) const; + bool isTied(const std::string& relative_path) const; + + /** Bind another node to an external source. */ + bool tie(const char* relative_path, const SGRawValue &rawValue, bool useDefault = true); + bool tie(const char* relative_path, const SGRawValue &rawValue, bool useDefault = true); + bool tie(const char* relative_path, const SGRawValue &rawValue, bool useDefault = true); + bool tie(const char* relative_path, const SGRawValue &rawValue, bool useDefault = true); + bool tie(const char* relative_path, const SGRawValue &rawValue, bool useDefault = true); + bool tie(const char* relative_path, const SGRawValue &rawValue, bool useDefault = true); + + bool tie(const std::string& relative_path, const SGRawValue &rawValue, bool useDefault = true); + bool tie(const std::string& relative_path, const SGRawValue &rawValue, bool useDefault = true); + bool tie(const std::string& relative_path, const SGRawValue &rawValue, bool useDefault = true); + bool tie(const std::string& relative_path, const SGRawValue &rawValue, bool useDefault = true); + bool tie(const std::string& relative_path, const SGRawValue &rawValue, bool useDefault = true); + bool tie(const std::string& relative_path, const SGRawValue &rawValue, bool useDefault = true); + + /** Unbind another node from any external data source. */ + bool untie(const char* relative_path); + bool untie(const std::string& relative_path); + + /** + * Add a change listener to the property. If "initial" is set call the + * listener initially. + */ + void addChangeListener(SGPropertyChangeListener* listener, bool initial = false); + + /** Remove a change listener from the property. */ + void removeChangeListener(SGPropertyChangeListener* listener); + + /** Get the number of listeners. */ + int nListeners() const; + + /** Fire a value change event to all listeners. */ + void fireValueChanged(); + + /** Fire a child-added event to all listeners. */ + void fireChildAdded(SGPropertyNode* child); + + /** + * Trigger a child-added and value-changed event for every child (Unlimited + * depth). + * + * @param fire_self Whether to trigger the events also for the node itself. + * + * It can be used to simulating the creation of a property tree, eg. for + * (re)initializing a subsystem which is controlled through the property tree. + */ + void fireCreatedRecursive(bool fire_self = false); + + /** Fire a child-removed event to all listeners. */ + void fireChildRemoved(SGPropertyNode* child); + + /** + * Fire a child-removed event for every child of this node (Unlimited depth) + * + * Upon removal of a child node only for this single node a child-removed + * event is triggered. If eg. resource cleanup relies on receiving a + * child-removed event for every child this method can be used. + */ + void fireChildrenRemovedRecursive(); + + /** Clear any existing value and set the type to NONE. */ + void clearValue(); + + /** + * Compare two property trees. The property trees are equal if: 1) + * They have no children, and have the same type and the values are + * equal, or 2) have the same number of children, and the + * corresponding children in each tree are equal. "corresponding" + * means have the same name and index. + * + * Attributes, removed children, and aliases aren't considered. + */ + static bool compare(const SGPropertyNode& lhs, const SGPropertyNode& rhs); protected: - void fireValueChanged (SGPropertyNode * node); - void fireChildAdded (SGPropertyNode * parent, SGPropertyNode * child); - void fireChildRemoved (SGPropertyNode * parent, SGPropertyNode * child); + /* fire*() generally need to temporarily modify _listeners->_num_iterators + so take exclusive lock rather than shared. */ - SGPropertyNode_ptr eraseChild(simgear::PropertyList::iterator child); + /** Protected constructor for making new nodes on demand. */ + SGPropertyNode(const std::string& name, int index, SGPropertyNode* parent); - /** - * Protected constructor for making new nodes on demand. - */ - SGPropertyNode (const std::string& name, int index, SGPropertyNode * parent); - template - SGPropertyNode (Itr begin, Itr end, int index, SGPropertyNode * parent); + template + SGPropertyNode(Itr begin, Itr end, int index, SGPropertyNode* parent); - static simgear::PropertyInterpolationMgr* _interpolation_mgr; + static simgear::PropertyInterpolationMgr* _interpolation_mgr; private: - // Get the raw value - bool get_bool () const; - int get_int () const; - long get_long () const; - float get_float () const; - double get_double () const; - const char * get_string () const; + // very internal path parsing function + template + friend SGPropertyNode* find_node_aux(SGPropertyNode* current, SplitItr& itr, bool create, int last_index); + + // Template-style getValue when lock is already held. + template + T getValue( + SGPropertyLock& lock, + typename std::enable_if::Internal>::type* dummy = 0 + ) const; + + // For SGVec3d and SGVec4d. + template + T getValue( + SGPropertyLock& lock, + typename std::enable_if::Internal>::type* dummy = 0 + ) const; - // Set the raw value - bool set_bool (bool value); - bool set_int (int value); - bool set_long (long value); - bool set_float (float value); - bool set_double (double value); - bool set_string (const char * value); + // For boost + friend size_t hash_value(const SGPropertyNode& node); + // Allow implementation code access to _mutex. + // + friend SGPropertyLock; + friend SGPropertyLockShared; + friend SGPropertyLockExclusive; + + // Misc implementation access. + friend SGPropertyNodeImpl; - /** - * Get the value as a string. - */ - const char * make_string () const; + // Class data. + // + + // Support for thread-safety. + // + mutable std::shared_mutex _mutex; + int _mutex_debug_shared = 0; + int _mutex_debug_exclusive = 0; + + // Core data. + // + int _index; + std::string _name; + SGPropertyNode* _parent; + simgear::PropertyList _children; + mutable std::string _buffer; + simgear::props::Type _type; + bool _tied; + int _attr = NO_ATTR; - /** - * Trace a read access. - */ - void trace_read () const; + // The right kind of pointer... + union { + SGPropertyNode* alias; + SGRaw* val; + } _value; + union { + bool bool_val; + int int_val; + long long_val; + float float_val; + double double_val; + char* string_val; + } _local_val; - /** - * Trace a write access. - */ - void trace_write () const; - - int _index; - std::string _name; - /// To avoid cyclic reference counting loops this shall not be a reference - /// counted pointer - SGPropertyNode * _parent; - simgear::PropertyList _children; - mutable std::string _buffer; - simgear::props::Type _type; - bool _tied; - int _attr = NO_ATTR; - - // The right kind of pointer... - union { - SGPropertyNode * alias; - SGRaw* val; - } _value; - - union { - bool bool_val; - int int_val; - long long_val; - float float_val; - double double_val; - char * string_val; - } _local_val; - - SGPropertyNodeListeners* _listeners; - - // Pass name as a pair of iterators - template - SGPropertyNode * getChildImpl (Itr begin, Itr end, int index = 0, bool create = false); - // very internal method - template - SGPropertyNode* getExistingChild (Itr begin, Itr end, int index); - // very internal path parsing function - template - friend SGPropertyNode* find_node_aux(SGPropertyNode * current, SplitItr& itr, - bool create, int last_index); - // For boost - friend size_t hash_value(const SGPropertyNode& node); + SGPropertyNodeListeners* _listeners; }; // Convenience functions for use in templates @@ -1886,13 +1433,22 @@ typename std::enable_if::value, T>::type getValue(const SGPropertyNode*); template<> -inline bool getValue(const SGPropertyNode* node) { return node->getBoolValue(); } +inline bool getValue(const SGPropertyNode* node) +{ + return node->getBoolValue(); +} template<> -inline int getValue(const SGPropertyNode* node) { return node->getIntValue(); } +inline int getValue(const SGPropertyNode* node) +{ + return node->getIntValue(); +} template<> -inline long getValue(const SGPropertyNode* node) { return node->getLongValue(); } +inline long getValue(const SGPropertyNode* node) +{ + return node->getLongValue(); +} template<> inline float getValue(const SGPropertyNode* node) @@ -1907,9 +1463,9 @@ inline double getValue(const SGPropertyNode* node) } template<> -inline const char * getValue(const SGPropertyNode* node) +inline const char* getValue(const SGPropertyNode* node) { - return node->getStringValue (); + return node->getStringValue(); } template<> @@ -1944,7 +1500,7 @@ namespace simgear }; } // namespace simgear -/** Extract enum from SGPropertyNode */ +/** Extract enum from SGPropertyNode. */ template #if PROPS_STANDALONE inline T @@ -1998,137 +1554,13 @@ inline bool setValue(SGPropertyNode* node, const char* value) return node->setStringValue(value); } -inline bool setValue (SGPropertyNode* node, const std::string& value) +inline bool setValue(SGPropertyNode* node, const std::string& value) { return node->setStringValue(value.c_str()); } -template -bool SGPropertyNode::tie(const SGRawValue &rawValue, bool useDefault) -{ - using namespace simgear::props; - if (_type == ALIAS || _tied) - return false; - - useDefault = useDefault && hasValue(); - T old_val = SGRawValue::DefaultValue(); - if (useDefault) - old_val = getValue(this); - clearValue(); - if (PropertyTraits::Internal) - _type = PropertyTraits::type_tag; - else - _type = EXTENDED; - _tied = true; - _value.val = rawValue.clone(); - if (useDefault) { - int save_attributes = getAttributes(); - setAttribute( WRITE, true ); - setValue(old_val); - setAttributes( save_attributes ); - } - return true; -} - -template<> -bool SGPropertyNode::tie (const SGRawValue &rawValue, - bool useDefault); - -template -T SGPropertyNode::getValue(typename std::enable_if::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()); - default: // avoid compiler warning - break; - } - return SGRawValue::DefaultValue(); -} - -template -inline T SGPropertyNode::getValue(typename std::enable_if::Internal>::type* dummy) const -{ - return ::getValue(this); -} - -template // TODO use C++11 or traits -std::vector SGPropertyNode::getChildValues(const std::string& name) const -{ - const simgear::PropertyList& props = getChildren(name); - std::vector values( props.size() ); - - for( size_t i = 0; i < props.size(); ++i ) - values[i] = props[i]->getValue(); - - return values; -} - -template -inline -std::vector SGPropertyNode::getChildValues(const std::string& name) const -{ - return getChildValues(name); -} - -template -bool SGPropertyNode::setValue(const T& val, - typename std::enable_if::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 std::enable_if::Internal>::type* dummy) -{ - return ::setValue(this, val); -} - -/** - * Utility function for creation of a child property node. - */ -inline SGPropertyNode* makeChild(SGPropertyNode* parent, const char* name, - int index = 0) +/** Utility function for creation of a child property node. */ +inline SGPropertyNode* makeChild(SGPropertyNode* parent, const char* name, int index = 0) { return parent->getChild(name, index, true); } @@ -2187,8 +1619,8 @@ struct Hash return hash_value(*node); } }; -} -} +} // namespace props +} // namespace simgear /** Convenience class for change listener callbacks without * creating a derived class implementing a "valueChanged" method. @@ -2199,16 +1631,23 @@ class SGPropertyChangeCallback : public SGPropertyChangeListener { public: - SGPropertyChangeCallback(T* obj, void (T::*method)(SGPropertyNode*), - SGPropertyNode_ptr property,bool initial=false) - : _obj(obj), _callback(method), _property(property) + SGPropertyChangeCallback(T* obj, + void (T::*method)(SGPropertyNode*), + SGPropertyNode_ptr property, + bool initial=false + ) + : _obj(obj), + _callback(method), + _property(property) { _property->addChangeListener(this,initial); } SGPropertyChangeCallback(const SGPropertyChangeCallback& other) : SGPropertyChangeListener(other), - _obj(other._obj), _callback(other._callback), _property(other._property) + _obj(other._obj), + _callback(other._callback), + _property(other._property) { _property->addChangeListener(this,false); } @@ -2217,10 +1656,12 @@ public: { _property->removeChangeListener(this); } - void valueChanged (SGPropertyNode * node) + + void valueChanged(SGPropertyNode* node) { (_obj->*_callback)(node); } + private: T* _obj; void (T::*_callback)(SGPropertyNode*);