From d455f5f4458ce7c446807bcc5d62e48c9186cb1b Mon Sep 17 00:00:00 2001 From: Florent Rougon Date: Tue, 10 Oct 2017 15:34:04 +0200 Subject: [PATCH 01/27] props_io.cxx: use SG_ORIGIN when throwing exceptions This will provide more accurate info than the fixed string "SimGear Property Reader". --- simgear/props/props_io.cxx | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/simgear/props/props_io.cxx b/simgear/props/props_io.cxx index 12e2b9e8..a0e0b90b 100644 --- a/simgear/props/props_io.cxx +++ b/simgear/props/props_io.cxx @@ -161,8 +161,7 @@ setFlag( int& mode, string message = "Unrecognized flag value '"; message += flag; message += '\''; - // FIXME: add location info - throw sg_io_exception(message, location, "SimGear Property Reader"); + throw sg_io_exception(message, location, SG_ORIGIN); } } @@ -177,7 +176,7 @@ PropsVisitor::startElement (const char * name, const XMLAttributes &atts) string message = "Root element name is "; message += name; message += "; expected PropertyList"; - throw sg_io_exception(message, location, "SimGear Property Reader"); + throw sg_io_exception(message, location, SG_ORIGIN); } // Check for an include. @@ -189,8 +188,7 @@ PropsVisitor::startElement (const char * name, const XMLAttributes &atts) { string message ="Cannot open file "; message += attval; - throw sg_io_exception(message, location, - "SimGear Property Reader"); + throw sg_io_exception(message, location, SG_ORIGIN); } readProperties(path, _root, 0, _extended); } catch (sg_io_exception &e) { @@ -277,7 +275,7 @@ PropsVisitor::startElement (const char * name, const XMLAttributes &atts) { string message ="Cannot open file "; message += val; - throw sg_io_exception(message, location, "SimGear Property Reader"); + throw sg_io_exception(message, location, SG_ORIGIN); } readProperties(path, node, 0, _extended); } @@ -352,8 +350,7 @@ PropsVisitor::endElement (const char * name) string message = "Unrecognized data type '"; message += st.type; message += '\''; - // FIXME: add location information - throw sg_io_exception(message, location, "SimGear Property Reader"); + throw sg_io_exception(message, location, SG_ORIGIN); } if( !ret ) SG_LOG @@ -770,7 +767,7 @@ copyPropertyValue(const SGPropertyNode *in, SGPropertyNode *out) break; string message = "Unknown internal SGPropertyNode type"; message += in->getType(); - throw sg_error(message, "SimGear Property Reader"); + throw sg_error(message, SG_ORIGIN); } return retval; From b57dca66be8a0015e22018dac1c0e4dd3a6a510f Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Fri, 13 Oct 2017 17:35:21 +0100 Subject: [PATCH 02/27] Handle case where no tranform matrix exists. --- simgear/canvas/elements/CanvasElement.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/simgear/canvas/elements/CanvasElement.cxx b/simgear/canvas/elements/CanvasElement.cxx index 4fb3eab8..a8557a06 100644 --- a/simgear/canvas/elements/CanvasElement.cxx +++ b/simgear/canvas/elements/CanvasElement.cxx @@ -433,6 +433,7 @@ namespace canvas osg::Vec2f Element::posToLocal(const osg::Vec2f& pos) const { getMatrix(); + if (! _transform) return osg::Vec2f(pos[0], pos[1]); const osg::Matrix& m = _transform->getInverseMatrix(); return osg::Vec2f ( From 7be1fcc32e4bfa7e48bd143ecee778b69afa85ee Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Sun, 15 Oct 2017 16:54:36 +0200 Subject: [PATCH 03/27] Add method to copy entire properry subtree --- simgear/props/props.cxx | 107 +++++++++++++++++++++++++--------------- simgear/props/props.hxx | 5 ++ 2 files changed, 71 insertions(+), 41 deletions(-) diff --git a/simgear/props/props.cxx b/simgear/props/props.cxx index 49dbdad8..b0dbd10c 100644 --- a/simgear/props/props.cxx +++ b/simgear/props/props.cxx @@ -2443,7 +2443,8 @@ SGPropertyNode::fireValueChanged (SGPropertyNode * node) { if (_listeners != 0) { for (unsigned int i = 0; i < _listeners->size(); i++) { - (*_listeners)[i]->valueChanged(node); + if ((*_listeners)[i]) + (*_listeners)[i]->valueChanged(node); } } if (_parent != 0) @@ -2581,53 +2582,77 @@ std::ostream& SGRawBase::printOn(std::ostream& stream) const namespace simgear { #if !PROPS_STANDALONE -template<> -std::istream& readFrom(std::istream& stream, SGVec4d& result) -{ - for (int i = 0; i < 4; ++i) { - stream >> result[i]; + template<> + std::istream& readFrom(std::istream& stream, SGVec4d& result) + { + for (int i = 0; i < 4; ++i) { + stream >> result[i]; + } + return stream; } - return stream; -} #endif -namespace -{ -bool compareNodeValue(const SGPropertyNode& lhs, const SGPropertyNode& rhs) -{ - props::Type ltype = lhs.getType(); - props::Type rtype = rhs.getType(); - if (ltype != rtype) - return false; - switch (ltype) { - case props::NONE: - return true; - case props::ALIAS: - return false; // XXX Should we look in aliases? - case props::BOOL: - return lhs.getValue() == rhs.getValue(); - case props::INT: - return lhs.getValue() == rhs.getValue(); - case props::LONG: - return lhs.getValue() == rhs.getValue(); - case props::FLOAT: - return lhs.getValue() == rhs.getValue(); - case props::DOUBLE: - return lhs.getValue() == rhs.getValue(); - case props::STRING: - case props::UNSPECIFIED: - return !strcmp(lhs.getStringValue(), rhs.getStringValue()); + namespace + { + bool compareNodeValue(const SGPropertyNode& lhs, const SGPropertyNode& rhs) + { + props::Type ltype = lhs.getType(); + props::Type rtype = rhs.getType(); + if (ltype != rtype) + return false; + switch (ltype) { + case props::NONE: + return true; + case props::ALIAS: + return false; // XXX Should we look in aliases? + case props::BOOL: + return lhs.getValue() == rhs.getValue(); + case props::INT: + return lhs.getValue() == rhs.getValue(); + case props::LONG: + return lhs.getValue() == rhs.getValue(); + case props::FLOAT: + return lhs.getValue() == rhs.getValue(); + case props::DOUBLE: + return lhs.getValue() == rhs.getValue(); + case props::STRING: + case props::UNSPECIFIED: + return !strcmp(lhs.getStringValue(), rhs.getStringValue()); #if !PROPS_STANDALONE - case props::VEC3D: - return lhs.getValue() == rhs.getValue(); - case props::VEC4D: - return lhs.getValue() == rhs.getValue(); + case props::VEC3D: + return lhs.getValue() == rhs.getValue(); + case props::VEC4D: + return lhs.getValue() == rhs.getValue(); #endif - default: - return false; + default: + return false; + } + } } } -} + + +void SGPropertyNode::copy(SGPropertyNode *to) +{ + if (nChildren()) + { + for (int i = 0; i < nChildren(); i++) { + SGPropertyNode *child = getChild(i); + SGPropertyNode *to_child = to->getChild(child->getName()); + if (!to_child) + to_child = to->addChild(child->getName()); + if (child->nChildren()) + { + child->copy(to_child); + } + else + { + to_child->setValue(child->getStringValue()); + } + } + } + else + to->setValue(getStringValue()); } bool SGPropertyNode::compare(const SGPropertyNode& lhs, diff --git a/simgear/props/props.hxx b/simgear/props/props.hxx index 620928b5..1f56e80e 100644 --- a/simgear/props/props.hxx +++ b/simgear/props/props.hxx @@ -1105,6 +1105,11 @@ public: */ SGPropertyNode * getNode (const char * relative_path, bool create = false); + /** + * deep copy one node to another. + */ + void copy(SGPropertyNode *to); + /** * Get a pointer to another node by relative path. */ From 7a374c43dc88e6717a068a4ee2254bff9d433349 Mon Sep 17 00:00:00 2001 From: Florent Rougon Date: Thu, 19 Oct 2017 10:40:17 +0200 Subject: [PATCH 04/27] Fix CMake test for std::isnan() It used the wrong header; std::isnan() is defined in . --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c25884a..e15718b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -343,7 +343,7 @@ SET(CMAKE_MINSIZEREL_POSTFIX "" CACHE STRING "add a postfix, usually empty on wi # isnan might not be real symbol, so can't check using function_exists check_cxx_source_compiles( - "#include + "#include int main() { return std::isnan(0.0);} " HAVE_STD_ISNAN) From 880c063d0411400cf71a25e20695a17927cac3e9 Mon Sep 17 00:00:00 2001 From: Florent Rougon Date: Fri, 27 Oct 2017 20:49:17 +0200 Subject: [PATCH 05/27] Remove useless readdir() calls in Dir::isEmpty() simgear::Dir::isEmpty() used to make up to 5 calls to readdir(), while 3 are enough to say whether the directory has entries other than '.' and '..'. Also add an automated test for this method. --- simgear/misc/sg_dir.cxx | 5 ++++- simgear/misc/sg_dir_test.cxx | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/simgear/misc/sg_dir.cxx b/simgear/misc/sg_dir.cxx index 79a6ff97..9430e1b2 100644 --- a/simgear/misc/sg_dir.cxx +++ b/simgear/misc/sg_dir.cxx @@ -296,7 +296,10 @@ bool Dir::isEmpty() const int n = 0; dirent* d; - while( (d = readdir(dp)) !=NULL && (n < 4) ) n++; + while (n < 3 && (d = readdir(dp)) != nullptr) { + n++; + } + closedir(dp); return (n == 2); // '.' and '..' always exist diff --git a/simgear/misc/sg_dir_test.cxx b/simgear/misc/sg_dir_test.cxx index 4c6526ae..dd6cbac8 100644 --- a/simgear/misc/sg_dir_test.cxx +++ b/simgear/misc/sg_dir_test.cxx @@ -2,6 +2,7 @@ #include +#include #include #include #include "sg_dir.hxx" @@ -34,11 +35,35 @@ void test_tempDir() d.remove(); } +void test_isEmpty() +{ + simgear::Dir d = simgear::Dir::tempDir("FlightGear"); + SG_VERIFY(!d.isNull() && d.exists() && d.isEmpty()); + SGPath f = d.file("some file"); + + { sg_ofstream file(f); } // create and close the file + SG_VERIFY(!d.isEmpty()); + + f.remove(); + SG_VERIFY(d.isEmpty()); + + simgear::Dir subDir{d.file("some subdir")}; + subDir.create(0777); + SG_VERIFY(!d.isEmpty()); + + subDir.remove(); + SG_VERIFY(d.isEmpty()); + + d.remove(); + SG_VERIFY(d.isEmpty()); // eek, but that's how it is +} + int main(int argc, char **argv) { test_isNull(); test_setRemoveOnDestroy(); test_tempDir(); + test_isEmpty(); return EXIT_SUCCESS; } From 2a60e5e338528f9431c0c27e59610a7a54f4432e Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Sun, 29 Oct 2017 01:04:36 +0200 Subject: [PATCH 06/27] Added touch animation. Designed for 2d objects, such as a canvas placements, this permits the receipt of touch (mouse click) events to enable the simulation of avionics with a touchscreen. The coordinates are passed in as arguments to the action; these can be accessed with Nasal via the cmdarg() method. example: touch true VSDImage 0 false nasal --- simgear/scene/model/SGPickAnimation.cxx | 154 ++++++++++++++++++++++ simgear/scene/model/SGPickAnimation.hxx | 14 ++ simgear/scene/model/SGReaderWriterXML.cxx | 2 +- simgear/scene/model/animation.cxx | 3 + 4 files changed, 172 insertions(+), 1 deletion(-) diff --git a/simgear/scene/model/SGPickAnimation.cxx b/simgear/scene/model/SGPickAnimation.cxx index 7b4c4d35..3fa643d6 100644 --- a/simgear/scene/model/SGPickAnimation.cxx +++ b/simgear/scene/model/SGPickAnimation.cxx @@ -832,3 +832,157 @@ SGSliderAnimation::setupCallbacks(SGSceneUserData* ud, osg::Group*) { ud->setPickCallback(new KnobSliderPickCallback(getConfig(), getModelRoot())); } + +/* + * touch screen is a 2d surface that will pass parameters to the callbacks indicating the + * normalized coordinates of hover or touch. Touch is defined as a button click. + * For compatibility with touchscreen operations this class does not differentiate between + * which buttons are touched, simply because this isn't how touchscreens work. + * Some touchscreens (e.g. SAW) can have a Z-axis indicating the pressure. This is not + * simulated. + */ + + +/** +* Handle picking events on object with a canvas placed onto +*/ +class TouchPickCallback : public SGPickCallback { + public: + TouchPickCallback(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + _repeatable(configNode->getBoolValue("repeatable", false)), + _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1)), + SGPickCallback(PriorityPanel) + { + std::vector bindings; + + bindings = configNode->getChildren("touch"); + for (unsigned int i = 0; i < bindings.size(); ++i) { + _touches.insert(bindings[i]->getIntValue()); + } + + _bindingsTouched = readBindingList(configNode->getChildren("binding"), modelRoot); + readOptionalBindingList(configNode, modelRoot, "mod-up", _bindingsReleased); + + if (configNode->hasChild("cursor")) { + _cursorName = configNode->getStringValue("cursor"); + } + } + + void addHoverBindings(const SGPropertyNode* hoverNode, + SGPropertyNode* modelRoot) + { + _hover = readBindingList(hoverNode->getChildren("binding"), modelRoot); + } + + virtual bool buttonPressed(int touchIdx, + const osgGA::GUIEventAdapter& event, + const Info& info) + { + if (_touches.find(touchIdx) == _touches.end()) { + return false; + } + + if (!anyBindingEnabled(_bindingsTouched)) { + return false; + } + SGPropertyNode_ptr params(new SGPropertyNode); + params->setDoubleValue("x", info.uv[0]); + params->setDoubleValue("y", info.uv[1]); + + _repeatTime = -_repeatInterval; // anti-bobble: delay start of repeat + fireBindingList(_bindingsTouched, params.ptr()); + return true; + } + virtual void buttonReleased(int keyModState, + const osgGA::GUIEventAdapter&, + const Info* info) + { + SG_UNUSED(keyModState); + SGPropertyNode_ptr params(new SGPropertyNode); + params->setDoubleValue("x", info->uv[0]); + params->setDoubleValue("y", info->uv[1]); + fireBindingList(_bindingsReleased, params.ptr()); + } + + virtual void update(double dt, int keyModState) + { + SG_UNUSED(keyModState); + if (!_repeatable) + return; + + _repeatTime += dt; + while (_repeatInterval < _repeatTime) { + _repeatTime -= _repeatInterval; + fireBindingList(_bindingsTouched); + } + } + + virtual bool hover(const osg::Vec2d& windowPos, + const Info& info) + { + if (!anyBindingEnabled(_hover)) { + return false; + } + + SGPropertyNode_ptr params(new SGPropertyNode); + params->setDoubleValue("x", info.uv[0]); + params->setDoubleValue("y", info.uv[1]); + fireBindingList(_hover, params.ptr()); + return true; + } + + std::string getCursor() const + { + return _cursorName; + } + + virtual bool needsUV() const { return true; } + + private: + SGBindingList _bindingsTouched; + SGBindingList _bindingsReleased; + SGBindingList _hover; + std::set _touches; + std::string _cursorName; + bool _repeatable; + double _repeatInterval; + double _repeatTime; +}; + +SGTouchAnimation::SGTouchAnimation(simgear::SGTransientModelData &modelData) : + SGPickAnimation(modelData) +{ +} + +osg::Group* SGTouchAnimation::createMainGroup(osg::Group* pr) +{ + SGRotateTransform* transform = new SGRotateTransform(); + pr->addChild(transform); + return transform; +} + +void SGTouchAnimation::setupCallbacks(SGSceneUserData* ud, osg::Group*) +{ + TouchPickCallback* touchCb = NULL; + + // add actions that become macro and command invocations + std::vector actions; + actions = getConfig()->getChildren("action"); + for (unsigned int i = 0; i < actions.size(); ++i) { + touchCb = new TouchPickCallback(actions[i], getModelRoot()); + ud->addPickCallback(touchCb); + } + + if (getConfig()->hasChild("hovered")) { + if (!touchCb) { + // make a trivial PickCallback to hang the hovered off of + SGPropertyNode_ptr dummyNode(new SGPropertyNode); + touchCb = new TouchPickCallback(dummyNode.ptr(), getModelRoot()); + ud->addPickCallback(touchCb); + } + + touchCb->addHoverBindings(getConfig()->getNode("hovered"), getModelRoot()); + } +// ud->setPickCallback(new TouchPickCallback(getConfig(), getModelRoot())); +} diff --git a/simgear/scene/model/SGPickAnimation.hxx b/simgear/scene/model/SGPickAnimation.hxx index 580f02a7..00695467 100644 --- a/simgear/scene/model/SGPickAnimation.hxx +++ b/simgear/scene/model/SGPickAnimation.hxx @@ -114,5 +114,19 @@ private: SGSharedPtr _animationValue; }; +class SGTouchAnimation : public SGPickAnimation +{ +public: + SGTouchAnimation(simgear::SGTransientModelData &modelData); + + +protected: + virtual osg::Group* createMainGroup(osg::Group* pr); + + virtual void setupCallbacks(SGSceneUserData* ud, osg::Group* parent); + +private: +}; + #endif // of SG_SCENE_PICK_ANIMATION_HXX diff --git a/simgear/scene/model/SGReaderWriterXML.cxx b/simgear/scene/model/SGReaderWriterXML.cxx index e46e6921..4c6304ac 100644 --- a/simgear/scene/model/SGReaderWriterXML.cxx +++ b/simgear/scene/model/SGReaderWriterXML.cxx @@ -226,7 +226,7 @@ namespace { { string typeString(aNode->getStringValue("type")); // exclude these so we don't show yellow outlines in preview mode - return (typeString == "pick") || (typeString == "knob") || (typeString == "slider"); + return (typeString == "pick") || (typeString == "knob") || (typeString == "slider") || (typeString == "touch"); } }; diff --git a/simgear/scene/model/animation.cxx b/simgear/scene/model/animation.cxx index 3e7f4399..3d3302ff 100644 --- a/simgear/scene/model/animation.cxx +++ b/simgear/scene/model/animation.cxx @@ -497,6 +497,9 @@ SGAnimation::animate(simgear::SGTransientModelData &modelData) } else if (type == "slider") { SGSliderAnimation anim(modelData); anim.apply(modelData.getNode()); + } else if (type == "touch") { + SGTouchAnimation anim(modelData); + anim.apply(modelData.getNode()); } else if (type == "range") { SGRangeAnimation anim(modelData); anim.apply(modelData); From 2642299d772a3b88264d5fb05086817d0742ced7 Mon Sep 17 00:00:00 2001 From: James Turner Date: Sun, 29 Oct 2017 13:20:39 +0000 Subject: [PATCH 07/27] We require C++11 now, simplify this code. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This ensures wstring to std::string conversion is always available, needed for some HID work I’m doing on a branch,. --- simgear/misc/strutils.cxx | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/simgear/misc/strutils.cxx b/simgear/misc/strutils.cxx index f45e0ac8..aee6a49c 100644 --- a/simgear/misc/strutils.cxx +++ b/simgear/misc/strutils.cxx @@ -29,11 +29,8 @@ #include // strerror_r() and strerror_s() #include #include - -#if defined(HAVE_CPP11_CODECVT) - #include // new in C++11 -#endif - +#include + #include "strutils.hxx" #include @@ -656,26 +653,14 @@ static std::string convertWStringToMultiByte(DWORD encoding, const std::wstring& std::wstring convertUtf8ToWString(const std::string& a) { -#ifdef SG_WINDOWS - return convertMultiByteToWString(CP_UTF8, a); -#elif defined(HAVE_CPP11_CODECVT) std::wstring_convert, wchar_t> ucs2conv; return ucs2conv.from_bytes(a); -#else - return std::wstring(); -#endif } std::string convertWStringToUtf8(const std::wstring& w) { -#ifdef SG_WINDOWS - return convertWStringToMultiByte(CP_UTF8, w); -#elif defined(HAVE_CPP11_CODECVT) std::wstring_convert, wchar_t> ucs2conv; return ucs2conv.to_bytes(w); -#else - return std::string(); -#endif } std::string convertWindowsLocal8BitToUtf8(const std::string& a) From 84b636debce4f3582102a1ae7b238364c7612403 Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Tue, 31 Oct 2017 10:42:16 +0100 Subject: [PATCH 08/27] Add a frequency filter and a bitcrusher filetr --- simgear/sound/filters.cxx | 169 ++++++++++++++++++++++++++++++++++++++ simgear/sound/filters.hxx | 75 +++++++++++++++++ 2 files changed, 244 insertions(+) create mode 100644 simgear/sound/filters.cxx create mode 100644 simgear/sound/filters.hxx diff --git a/simgear/sound/filters.cxx b/simgear/sound/filters.cxx new file mode 100644 index 00000000..8b59bf4a --- /dev/null +++ b/simgear/sound/filters.cxx @@ -0,0 +1,169 @@ +/* + * Copyright 2007-2017 by Erik Hofman. + * Copyright 2009-2017 by Adalin B.V. + * + * This file is part of AeonWave + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the Lesser GNU General Public License as published + * by the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the Lesser GNU General Public License + * along with this program; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include + +#include +#include "filters.hxx" + +namespace simgear { + +FreqFilter::FreqFilter(int order, float sample_freq, float cutoff_freq, float Qfactor) { + + Q = Qfactor; + fs = sample_freq; + no_stages = order / 2; + gain = order; + + butterworth_compute(cutoff_freq); + + for (unsigned int i = 0; i < 2*SG_FREQFILTER_MAX_STAGES; ++i) { + hist[i] = 0.0f; + } +} + +FreqFilter::~FreqFilter() { +} + +void FreqFilter::update( int16_t *data, unsigned int num) { + + if (num) { + float k = gain; + for (unsigned int stage = 0; stage < no_stages; ++stage) { + + float h0 = hist[2*stage + 0]; + float h1 = hist[2*stage + 1]; + unsigned int i = num; + do { + float nsmp, smp = data[i] * k; + smp = smp + h0 * coeff[4*stage + 0]; + nsmp = smp + h1 * coeff[4*stage + 1]; + smp = nsmp + h0 * coeff[4*stage + 2]; + smp = smp + h1 * coeff[4*stage + 3]; + + h1 = h0; + h0 = nsmp; + data[i] = smp; + } + while (--i); + + hist[2*stage + 0] = h0; + hist[2*stage + 1] = h1; + k = 1.0f; + } + } +} + +inline void FreqFilter::bilinear(float a0, float a1, float a2, + float b0, float b1, float b2, + float *k, int stage) { + a2 *= 4.0f; + b2 *= 4.0f; + a1 *= 2.0f; + b1 *= 2.0f; + + float ad = a2 + a1 + a0; + float bd = b2 + b1 + b0; + + *k *= ad/bd; + + coeff[4*stage + 0] = 2.0f*(-b2 + b0) / bd; + coeff[4*stage + 1] = (b2 - b1 + b0) / bd; + coeff[4*stage + 2] = 2.0f*(-a2 + a0) / ad; + coeff[4*stage + 3] = (a2 - a1 + a0) / ad; + + // negate to prevent this is required every time the filter is applied. + coeff[4*stage + 0] = -coeff[4*stage + 0]; + coeff[4*stage + 1] = -coeff[4*stage + 1]; +} + +// convert from S-domain to Z-domain +inline void FreqFilter::bilinear_s2z(float *a0, float *a1, float *a2, + float *b0, float *b1, float *b2, + float fc, float fs, float *k, int stage) { + // prewarp + float wp = 2.0f*tanf(SG_PI * fc/fs); + + *a2 /= wp*wp; + *b2 /= wp*wp; + *a1 /= wp; + *b1 /= wp; + + bilinear(*a0, *a1, *a2, *b0, *b1, *b2, k, stage); +} + +void FreqFilter::butterworth_compute(float fc) { + + // http://www.ti.com/lit/an/sloa049b/sloa049b.pdf + static const float _Q[SG_FREQFILTER_MAX_STAGES][SG_FREQFILTER_MAX_STAGES]= { + { 0.7071f, 1.0f, 1.0f, 1.0f }, // 2nd order + { 0.5412f, 1.3605f, 1.0f, 1.0f }, // 4th order + { 0.5177f, 0.7071f, 1.9320f, 1.0f }, // 6th roder + { 0.5098f, 0.6013f, 0.8999f, 2.5628f } // 8th order + }; + + gain = 1.0f; + + int pos = no_stages-1; + for (unsigned i = 0; i < no_stages; ++i) + { + float a2 = 0.0f; + float a1 = 0.0f; + float a0 = 1.0f; + + float b2 = 1.0f; + float b1 = 1.0f/(_Q[pos][i] * Q); + float b0 = 1.0f; + + // fill the filter coefficients + bilinear_s2z(&a0, &a1, &a2, &b0, &b1, &b2, fc, fs, &gain, i); + } +} + + + +BitCrusher::BitCrusher(float level) { + + float bits = level * 15.0f; + factor = powf(2.0f, bits); + devider = 1.0f/factor; +} + +BitCrusher::~BitCrusher() { +} + +void BitCrusher::update( int16_t *data, unsigned int num ) { + + if (num && factor < 1.0f) { + unsigned int i = num; + do { + float integral; + + modff(data[i]*devider, &integral); + data[i] = integral*factor; + } while (--i); + } +} + +}; // namespace simgear + diff --git a/simgear/sound/filters.hxx b/simgear/sound/filters.hxx new file mode 100644 index 00000000..89af2f0a --- /dev/null +++ b/simgear/sound/filters.hxx @@ -0,0 +1,75 @@ +/* + * Copyright 2007-2017 by Erik Hofman. + * Copyright 2009-2017 by Adalin B.V. + * + * This file is part of AeonWave + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the Lesser GNU General Public License as published + * by the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the Lesser GNU General Public License + * along with this program; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _SIMGEAR_FREQUENCY_FILTER_HXX +#define _SIMGEAR_FREQUENCY_FILTER_HXX + +namespace simgear { + +// Every stage is a 2nd order filter +// Four stages therefore equals to an 8th order filter with a 48dB/oct slope. +#define SG_FREQFILTER_MAX_STAGES 4 + +class FreqFilter { + +private: + + float fs, Q, gain; + float coeff[4*SG_FREQFILTER_MAX_STAGES]; + float hist[2*SG_FREQFILTER_MAX_STAGES]; + unsigned char no_stages; + + void butterworth_compute(float fc); + void bilinear(float a0, float a1, float a2, + float b0, float b1, float b2, + float *k, int stage); + void bilinear_s2z(float *a0, float *a1, float *a2, + float *b0, float *b1, float *b2, + float fc, float fs, float *k, int stage); + +public: + + FreqFilter(int order, float fs, float cutoff, float Qfactor = 1.0f); + ~FreqFilter(); + + void update( int16_t *data, unsigned int num ); +}; + + + +class BitCrusher { + +private: + float factor, devider; + +public: + + // level ranges from 0.0f (all muted) to 1.0f (no change) + BitCrusher(float level); + ~BitCrusher(); + + void update( int16_t *data, unsigned int num ); +}; + +}; // namespace simgear + +#endif // _SIMGEAR_FREQUENCY_FILTER_HXX From c03359a18962ff92e3edccd5d07fcafa05906688 Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Tue, 31 Oct 2017 10:42:44 +0100 Subject: [PATCH 09/27] Update to the (now GPL) AeonWave version 3.0+ --- CMakeModules/FindAAX.cmake | 21 +++++++++++++++++---- SimGearConfig.cmake.in | 1 + simgear/sound/CMakeLists.txt | 10 +++++++--- simgear/sound/aeonwave_test1.cxx | 12 ++++++------ simgear/sound/soundmgr_aeonwave.cxx | 19 ++++++++++--------- 5 files changed, 41 insertions(+), 22 deletions(-) diff --git a/CMakeModules/FindAAX.cmake b/CMakeModules/FindAAX.cmake index d9a5e00b..4e0bfdcd 100644 --- a/CMakeModules/FindAAX.cmake +++ b/CMakeModules/FindAAX.cmake @@ -10,12 +10,13 @@ # # Created by Erik Hofman. -FIND_PATH(AAX_INCLUDE_DIR aax/aeonwave.hpp +FIND_PATH(AAX_INCLUDE_DIR aax/aax.h HINTS $ENV{AAXDIR} $ENV{ProgramFiles}/aax $ENV{ProgramFiles}/AeonWave $ENV{ProgramFiles}/Adalin/AeonWave + ${CMAKE_SOURCE_DIR}/aax PATH_SUFFIXES include PATHS ~/Library/Frameworks @@ -26,23 +27,35 @@ FIND_PATH(AAX_INCLUDE_DIR aax/aeonwave.hpp ) FIND_LIBRARY(AAX_LIBRARY - NAMES AAX aax AAX32 libAAX32 + NAMES AAX aax AAX32 HINTS $ENV{AAXDIR} $ENV{ProgramFiles}/AAX $ENV{ProgramFiles}/AeonWave $ENV{ProgramFiles}/Adalin/AeonWave + ${CMAKE_BUILD_DIR}/aax PATH_SUFFIXES bin lib lib/${CMAKE_LIBRARY_ARCHITECTURE} lib64 libs64 libs libs/Win32 libs/Win64 PATHS ~/Library/Frameworks /Library/Frameworks + /usr/local /usr /opt - /usr/local ) -SET(AAX_FOUND "NO") IF(AAX_LIBRARY AND AAX_INCLUDE_DIR) SET(AAX_FOUND "YES") +ELSE(AAX_LIBRARY AND AAX_INCLUDE_DIR) + IF(NOT AAX_INCLUDE_DIR) + MESSAGE(FATAL_ERROR "Unable to find the AAX library development files.") + SET(AAX_FOUND "NO") + ENDIF(NOT AAX_INCLUDE_DIR) + IF(NOT AAX_LIBRARY) + IF(SINGLE_PACKAGE) + SET(AAX_LIBRARY "${aax_BUILD_DIR}/aax/AAX32.dll") + SET(AAX_FOUND "YES") + ELSE(SINGLE_PACKAGE) + ENDIF(SINGLE_PACKAGE) + ENDIF(NOT AAX_LIBRARY) ENDIF(AAX_LIBRARY AND AAX_INCLUDE_DIR) diff --git a/SimGearConfig.cmake.in b/SimGearConfig.cmake.in index 8e93ad48..71be04cd 100644 --- a/SimGearConfig.cmake.in +++ b/SimGearConfig.cmake.in @@ -7,6 +7,7 @@ find_dependency(Threads) set(SIMGEAR_HEADLESS @SIMGEAR_HEADLESS@) set(SIMGEAR_SOUND @ENABLE_SOUND@) +set(USE_AEONWAVE @USE_AEONWAVE@) # OpenAL isn't a public dependency, so maybe not needed #if (SIMGEAR_SOUND) diff --git a/simgear/sound/CMakeLists.txt b/simgear/sound/CMakeLists.txt index 4c4408e5..b284d736 100644 --- a/simgear/sound/CMakeLists.txt +++ b/simgear/sound/CMakeLists.txt @@ -4,16 +4,15 @@ set(HEADERS sample.hxx sample_group.hxx xmlsound.hxx - readwav.hxx soundmgr.hxx + filters.hxx ) set(SOURCES sample.cxx sample_group.cxx xmlsound.cxx - readwav.cxx - soundmgr_openal_private.hxx + filters.cxx ) if (USE_AEONWAVE) @@ -21,8 +20,13 @@ if (USE_AEONWAVE) soundmgr_aeonwave.cxx ) else() + set(HEADERS ${HEADERS} + readwav.hxx + ) set(SOURCES ${SOURCES} soundmgr_openal.cxx + soundmgr_openal_private.hxx + readwav.cxx ) endif() diff --git a/simgear/sound/aeonwave_test1.cxx b/simgear/sound/aeonwave_test1.cxx index 1e0baa0d..fca3777c 100644 --- a/simgear/sound/aeonwave_test1.cxx +++ b/simgear/sound/aeonwave_test1.cxx @@ -66,15 +66,15 @@ int main( int argc, char *argv[] ) std::cout << "Vendor: " << _vendor << std::endl; std::cout << "Renderer: " << _renderer << std::endl; - aax::Matrix64 mtx64; - mtx64.translate(-5000.0, 12500.0, 1000.0); + aax::Matrix mtx; + mtx.translate(-5000.0, 12500.0, 1000.0); - aax::Matrix mtx = mtx64.toMatrix(); - emitter.matrix(mtx); + aax::Matrix64 mtx64 = mtx.toMatrix64(); + emitter.matrix(mtx64); mtx.translate(-5.0, 2.0, 1.0); - mtx.inverse(); - aax.sensor_matrix(mtx); + mtx64.inverse(); + aax.sensor_matrix(mtx64); aax.set(AAX_PLAYING); emitter.set(AAX_PLAYING); diff --git a/simgear/sound/soundmgr_aeonwave.cxx b/simgear/sound/soundmgr_aeonwave.cxx index 1ae801a5..0ba9d33b 100644 --- a/simgear/sound/soundmgr_aeonwave.cxx +++ b/simgear/sound/soundmgr_aeonwave.cxx @@ -92,14 +92,14 @@ public: } void init() { - _mtx = aax::Matrix(); + _mtx = aax::Matrix64(); } void update_pos_and_orientation() { SGVec3d sgv_at = _orientation.backTransform(-SGVec3d::e3()); SGVec3d sgv_up = _orientation.backTransform(SGVec3d::e2()); - SGVec3f pos = SGVec3f::zeros(); + SGVec3d pos = SGVec3d::zeros(); _mtx.set(pos.data(), toVec3f(sgv_at).data(), toVec3f(sgv_up).data()); @@ -107,7 +107,7 @@ public: } aax::AeonWave _aax; - aax::Matrix _mtx; + aax::Matrix64 _mtx; SGVec3d _absolute_pos; SGVec3d _base_pos; @@ -338,7 +338,7 @@ void SGSoundMgr::update( double dt ) TRY( dsp.set(AAX_SOUND_VELOCITY, 340.3f) ); TRY( d->_aax.set(dsp) ); #endif - aax::Matrix mtx = d->_mtx; + aax::Matrix64 mtx = d->_mtx; mtx.inverse(); TRY( d->_aax.sensor_matrix(mtx) ); @@ -465,7 +465,7 @@ unsigned int SGSoundMgr::request_buffer(SGSoundSample *sample) } bufid = d->_buffer_id++; - d->_buffers.insert( std::make_pair(bufid,buf) ); + d->_buffers.insert( {bufid, buf} ); if ( !sample->is_file() ) { enum aaxFormat format = AAX_FORMAT_NONE; @@ -493,7 +493,7 @@ unsigned int SGSoundMgr::request_buffer(SGSoundSample *sample) unsigned int no_samples = sample->get_no_samples(); unsigned int no_tracks = sample->get_no_tracks(); unsigned int frequency = sample->get_frequency(); - TRY( buf.set(d->_aax, no_samples, no_tracks, format) ); + buf.set(d->_aax, no_samples, no_tracks, format); TRY( buf.set(AAX_FREQUENCY, frequency) ); TRY( buf.fill(sample->get_data()) ); @@ -659,11 +659,11 @@ void SGSoundMgr::update_sample_config( SGSoundSample *sample, SGVec3d& position, aax::Emitter& emitter = d->get_emitter(sample->get_source()); aax::dsp dsp; - aax::Vector pos = toVec3f(position).data(); + aax::Vector64 pos = position.data(); aax::Vector ori = orientation.data(); aax::Vector vel = velocity.data(); - aax::Matrix mtx(pos, ori); + aax::Matrix64 mtx(pos, ori); TRY( emitter.matrix(mtx) ); TRY( emitter.velocity(vel) ); @@ -763,7 +763,8 @@ void SGSoundMgr::set_position( const SGVec3d& pos, const SGGeod& pos_geod ) SGVec3f SGSoundMgr::get_direction() const { - aaxVec3f pos, at, up; + aaxVec3f at, up; + aaxVec3d pos; d->_mtx.get(pos, at, up); return SGVec3f( at ); } From 2ac97a9f1f071a558bcfe1997feffef6d621cfca Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Tue, 31 Oct 2017 10:47:12 +0100 Subject: [PATCH 10/27] std::wstring_convert requires locale --- simgear/misc/strutils.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/simgear/misc/strutils.cxx b/simgear/misc/strutils.cxx index aee6a49c..27fae3df 100644 --- a/simgear/misc/strutils.cxx +++ b/simgear/misc/strutils.cxx @@ -30,6 +30,7 @@ #include #include #include +#include #include "strutils.hxx" From db89f0d4d17c10273865e76cc5c4b7df7b55eb5b Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Tue, 31 Oct 2017 10:49:04 +0100 Subject: [PATCH 11/27] Oops, use the proper project name --- simgear/sound/filters.cxx | 2 +- simgear/sound/filters.hxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/simgear/sound/filters.cxx b/simgear/sound/filters.cxx index 8b59bf4a..b8539c96 100644 --- a/simgear/sound/filters.cxx +++ b/simgear/sound/filters.cxx @@ -2,7 +2,7 @@ * Copyright 2007-2017 by Erik Hofman. * Copyright 2009-2017 by Adalin B.V. * - * This file is part of AeonWave + * This file is part of SimGear * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License as published diff --git a/simgear/sound/filters.hxx b/simgear/sound/filters.hxx index 89af2f0a..806c40d8 100644 --- a/simgear/sound/filters.hxx +++ b/simgear/sound/filters.hxx @@ -2,7 +2,7 @@ * Copyright 2007-2017 by Erik Hofman. * Copyright 2009-2017 by Adalin B.V. * - * This file is part of AeonWave + * This file is part of SimGear * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License as published From e482f04123a4972b83c049d7ece6ee88ba1fd270 Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Tue, 31 Oct 2017 11:22:39 +0100 Subject: [PATCH 12/27] int16_t needs cstdint --- simgear/sound/filters.hxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/simgear/sound/filters.hxx b/simgear/sound/filters.hxx index 806c40d8..4e153c4a 100644 --- a/simgear/sound/filters.hxx +++ b/simgear/sound/filters.hxx @@ -23,6 +23,8 @@ #ifndef _SIMGEAR_FREQUENCY_FILTER_HXX #define _SIMGEAR_FREQUENCY_FILTER_HXX +#include + namespace simgear { // Every stage is a 2nd order filter From 10956056b30706b43676c26d23b03a1b6d0efa04 Mon Sep 17 00:00:00 2001 From: James Turner Date: Wed, 1 Nov 2017 17:03:34 +0000 Subject: [PATCH 13/27] Own code for UTF-32 <-> UTF-8 conversion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoids codecvt dependency on Unix where it might not be present, eg with GCC 4.8; on Windows we use since it’s present in VS2015 to avoid writing a seperate UTF-16 <-> UTF-8 conversion. --- simgear/misc/strutils.cxx | 79 ++++++++++++++++++++++++++++++++-- simgear/misc/strutils_test.cxx | 15 +++++++ 2 files changed, 91 insertions(+), 3 deletions(-) diff --git a/simgear/misc/strutils.cxx b/simgear/misc/strutils.cxx index 27fae3df..e8117e6b 100644 --- a/simgear/misc/strutils.cxx +++ b/simgear/misc/strutils.cxx @@ -29,9 +29,8 @@ #include // strerror_r() and strerror_s() #include #include -#include -#include - +#include + #include "strutils.hxx" #include @@ -42,6 +41,8 @@ #if defined(SG_WINDOWS) #include + #include + #include #endif using std::string; @@ -654,14 +655,86 @@ static std::string convertWStringToMultiByte(DWORD encoding, const std::wstring& std::wstring convertUtf8ToWString(const std::string& a) { +#if defined(SG_WINDOWS) std::wstring_convert, wchar_t> ucs2conv; return ucs2conv.from_bytes(a); +#else + assert(sizeof(wchar_t) == 4); + std::wstring result; + int expectedContinuationCount = 0; + wchar_t wc = 0; + + for (uint8_t utf8CodePoint : a) { + // ASCII 7-bit range + if (utf8CodePoint <= 0x7f) { + if (expectedContinuationCount != 0) { + throw sg_format_exception(); + } + + result.push_back(static_cast(utf8CodePoint)); + } else if (expectedContinuationCount > 0) { + if ((utf8CodePoint & 0xC0) != 0x80) { + throw sg_format_exception(); + } + + wc = (wc << 6) | (utf8CodePoint & 0x3F); + if (--expectedContinuationCount == 0) { + result.push_back(wc); + } + } else { + if ((utf8CodePoint & 0xE0) == 0xC0) { + expectedContinuationCount = 1; + wc = utf8CodePoint & 0x1f; + } else if ((utf8CodePoint & 0xF0) == 0xE0) { + expectedContinuationCount = 2; + wc = utf8CodePoint & 0x0f; + } else if ((utf8CodePoint & 0xF8) == 0xF0) { + expectedContinuationCount = 3; + wc =utf8CodePoint & 0x07; + } else { + // illegal UTF-8 encoding + throw sg_format_exception(); + } + } + } // of UTF-8 code point iteration + + return result; + +#endif + } std::string convertWStringToUtf8(const std::wstring& w) { +#if defined(SG_WINDOWS) std::wstring_convert, wchar_t> ucs2conv; return ucs2conv.to_bytes(w); +#else + assert(sizeof(wchar_t) == 4); + std::string result; + + for (wchar_t cp : w) { + if (cp <= 0x7f) { + result.push_back(static_cast(cp)); + } else if (cp <= 0x07ff) { + result.push_back(0xC0 | ((cp >> 6) & 0x1f)); + result.push_back(0x80 | (cp & 0x3f)); + } else if (cp <= 0xffff) { + result.push_back(0xE0 | ((cp >> 12) & 0x0f)); + result.push_back(0x80 | ((cp >> 6) & 0x3f)); + result.push_back(0x80 | (cp & 0x3f)); + } else if (cp < 0x10ffff) { + result.push_back(0xF0 | ((cp >> 18) & 0x07)); + result.push_back(0x80 | ((cp >> 12) & 0x3f)); + result.push_back(0x80 | ((cp >> 6) & 0x3f)); + result.push_back(0x80 | (cp & 0x3f)); + } else { + throw sg_format_exception(); + } + } + + return result; +#endif } std::string convertWindowsLocal8BitToUtf8(const std::string& a) diff --git a/simgear/misc/strutils_test.cxx b/simgear/misc/strutils_test.cxx index 84978da5..3a5f2e72 100644 --- a/simgear/misc/strutils_test.cxx +++ b/simgear/misc/strutils_test.cxx @@ -605,6 +605,20 @@ void test_readTime() SG_CHECK_EQUAL_EP(strutils::readTime("-0:0:28"), -28 * seconds); } +void test_utf8Convert() +{ + // F, smiley emoticon, Maths summation symbol, section sign + std::wstring a(L"\u0046\U0001F600\u2211\u00A7"); + + + std::string utf8A = strutils::convertWStringToUtf8(a); + SG_VERIFY(utf8A == std::string("F\xF0\x9F\x98\x80\xE2\x88\x91\xC2\xA7")); + + + std::wstring aRoundTrip = strutils::convertUtf8ToWString(utf8A); + SG_VERIFY(a == aRoundTrip); +} + int main(int argc, char* argv[]) { test_strip(); @@ -624,6 +638,7 @@ int main(int argc, char* argv[]) test_error_string(); test_propPathMatch(); test_readTime(); + test_utf8Convert(); return EXIT_SUCCESS; } From 18f04842490dd6c60a9a595f394d97bb4f699a06 Mon Sep 17 00:00:00 2001 From: Florent Rougon Date: Sat, 11 Nov 2017 01:28:03 +0100 Subject: [PATCH 14/27] Convert structure/shared_ptr_test.cpp to use the SG test macros (no more boost) --- simgear/structure/CMakeLists.txt | 9 ++- simgear/structure/shared_ptr_test.cpp | 79 ++++++++++++++++----------- 2 files changed, 52 insertions(+), 36 deletions(-) diff --git a/simgear/structure/CMakeLists.txt b/simgear/structure/CMakeLists.txt index 0c41b622..9d25d3d7 100644 --- a/simgear/structure/CMakeLists.txt +++ b/simgear/structure/CMakeLists.txt @@ -60,14 +60,13 @@ add_executable(test_expressions expression_test.cxx) target_link_libraries(test_expressions ${TEST_LIBS}) add_test(expressions ${EXECUTABLE_OUTPUT_PATH}/test_expressions) +add_executable(test_shared_ptr shared_ptr_test.cpp) +target_link_libraries(test_shared_ptr ${TEST_LIBS}) +add_test(shared_ptr ${EXECUTABLE_OUTPUT_PATH}/test_shared_ptr) + endif(ENABLE_TESTS) add_boost_test(function_list SOURCES function_list_test.cxx LIBRARIES ${TEST_LIBS} ) - -add_boost_test(shared_ptr - SOURCES shared_ptr_test.cpp - LIBRARIES ${TEST_LIBS} -) diff --git a/simgear/structure/shared_ptr_test.cpp b/simgear/structure/shared_ptr_test.cpp index 877e1468..e99056b3 100644 --- a/simgear/structure/shared_ptr_test.cpp +++ b/simgear/structure/shared_ptr_test.cpp @@ -1,6 +1,11 @@ -/// Unit tests for reference counting and smart pointer classes -#define BOOST_TEST_MODULE structure -#include +// -*- coding: utf-8 -*- +// +// Unit tests for reference counting and smart pointer classes + +#include +#include // EXIT_SUCCESS + +#include #include "SGSharedPtr.hxx" #include "SGWeakPtr.hxx" @@ -21,33 +26,35 @@ struct ReferenceCounted: }; typedef SGSharedPtr RefPtr; -BOOST_AUTO_TEST_CASE( shared_ptr ) +void test_SGSharedPtr() { - BOOST_REQUIRE_EQUAL( ReferenceCounted::count(0), 0 ); + std::cout << "Testing SGSharedPtr and SGReferenced" << std::endl; + + SG_CHECK_EQUAL( ReferenceCounted::count(0), 0 ); RefPtr ptr( new ReferenceCounted() ); - BOOST_REQUIRE_EQUAL( instance_count, 1 ); - BOOST_REQUIRE_EQUAL( ReferenceCounted::count(ptr.get()), 1 ); - BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 1 ); + SG_CHECK_EQUAL( instance_count, 1 ); + SG_CHECK_EQUAL( ReferenceCounted::count(ptr.get()), 1 ); + SG_CHECK_EQUAL( ptr.getNumRefs(), 1 ); RefPtr ptr2 = ptr; - BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 2 ); - BOOST_REQUIRE_EQUAL( ptr2.getNumRefs(), 2 ); + SG_CHECK_EQUAL( ptr.getNumRefs(), 2 ); + SG_CHECK_EQUAL( ptr2.getNumRefs(), 2 ); - BOOST_REQUIRE_EQUAL( ptr, ptr2 ); - BOOST_REQUIRE_EQUAL( ptr.get(), ptr2.get() ); + SG_CHECK_EQUAL( ptr, ptr2 ); + SG_CHECK_EQUAL( ptr.get(), ptr2.get() ); ptr.reset(); - BOOST_REQUIRE( !ptr.get() ); - BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 0 ); - BOOST_REQUIRE_EQUAL( ReferenceCounted::count(ptr2.get()), 1 ); - BOOST_REQUIRE_EQUAL( ptr2.getNumRefs(), 1 ); + SG_CHECK_IS_NULL( ptr.get() ); + SG_CHECK_EQUAL( ptr.getNumRefs(), 0 ); + SG_CHECK_EQUAL( ReferenceCounted::count(ptr2.get()), 1 ); + SG_CHECK_EQUAL( ptr2.getNumRefs(), 1 ); ptr2.reset(); - BOOST_REQUIRE( !ptr2.get() ); - BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 0 ); - BOOST_REQUIRE_EQUAL( ptr2.getNumRefs(), 0 ); - BOOST_REQUIRE_EQUAL( instance_count, 0) ; + SG_CHECK_IS_NULL( ptr2.get() ); + SG_CHECK_EQUAL( ptr.getNumRefs(), 0 ); + SG_CHECK_EQUAL( ptr2.getNumRefs(), 0 ); + SG_CHECK_EQUAL( instance_count, 0) ; } class Base1: @@ -63,31 +70,41 @@ class VirtualDerived: public Base2 {}; -BOOST_AUTO_TEST_CASE( virtual_weak_ptr ) +void test_SGWeakPtr() { + std::cout << "Testing SGWeakPtr and SGVirtualWeakReferenced" << std::endl; + SGSharedPtr ptr( new VirtualDerived() ); SGWeakPtr weak_ptr( ptr ); - BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 1 ); + SG_CHECK_EQUAL( ptr.getNumRefs(), 1 ); SGSharedPtr ptr1( weak_ptr.lock() ); - BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 2 ); + SG_CHECK_EQUAL( ptr.getNumRefs(), 2 ); // converting constructor - BOOST_REQUIRE_EQUAL( SGSharedPtr(weak_ptr), ptr1 ); + SG_CHECK_EQUAL( SGSharedPtr(weak_ptr), ptr1 ); SGSharedPtr ptr2( weak_ptr.lock() ); - BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 3 ); + SG_CHECK_EQUAL( ptr.getNumRefs(), 3 ); - BOOST_REQUIRE( ptr != NULL ); - BOOST_REQUIRE_EQUAL( ptr.get(), ptr1.get() ); - BOOST_REQUIRE_EQUAL( ptr.get(), ptr2.get() ); + SG_CHECK_IS_NOT_NULL( ptr ); + SG_CHECK_EQUAL( ptr.get(), ptr1.get() ); + SG_CHECK_EQUAL( ptr.get(), ptr2.get() ); SGWeakPtr weak_base1( ptr ); SGWeakPtr weak_base2( ptr ); ptr1 = dynamic_cast(weak_base1.lock().get()); ptr2 = dynamic_cast(weak_base2.lock().get()); - BOOST_REQUIRE_EQUAL( ptr.get(), ptr1.get() ); - BOOST_REQUIRE_EQUAL( ptr.get(), ptr2.get() ); - BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 3 ); + SG_CHECK_EQUAL( ptr.get(), ptr1.get() ); + SG_CHECK_EQUAL( ptr.get(), ptr2.get() ); + SG_CHECK_EQUAL( ptr.getNumRefs(), 3 ); +} + +int main(int argc, char* argv[]) +{ + test_SGSharedPtr(); + test_SGWeakPtr(); + + return EXIT_SUCCESS; } From fedafb9352c2877c5983e90834ffba0fff80ff15 Mon Sep 17 00:00:00 2001 From: Florent Rougon Date: Thu, 9 Nov 2017 21:08:00 +0100 Subject: [PATCH 15/27] SGSharedPtr: add move constructor and move assignment operator --- simgear/structure/SGSharedPtr.hxx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/simgear/structure/SGSharedPtr.hxx b/simgear/structure/SGSharedPtr.hxx index 3afcfc91..181dcbb6 100644 --- a/simgear/structure/SGSharedPtr.hxx +++ b/simgear/structure/SGSharedPtr.hxx @@ -56,6 +56,8 @@ public: { get(_ptr); } SGSharedPtr(const SGSharedPtr& p) : _ptr(p.get()) { get(_ptr); } + SGSharedPtr(SGSharedPtr&& other) : SGSharedPtr() + { swap(other); } template SGSharedPtr(const SGSharedPtr& p) : _ptr(p.get()) { get(_ptr); } @@ -64,9 +66,11 @@ public: { reset(p.lock().get()); } ~SGSharedPtr(void) { reset(); } - - SGSharedPtr& operator=(const SGSharedPtr& p) - { reset(p.get()); return *this; } + + // This handles copy assignment using the copy-and-swap idiom, and also move + // assignment thanks to the move construtor. + SGSharedPtr& operator=(SGSharedPtr p) + { swap(p); return *this; } template SGSharedPtr& operator=(const SGSharedPtr& p) { reset(p.get()); return *this; } From f22b9ba9f1c6910b214d87cacf2d8107541bd594 Mon Sep 17 00:00:00 2001 From: Florent Rougon Date: Sat, 11 Nov 2017 13:31:42 +0100 Subject: [PATCH 16/27] SGSharedPtr: add unit tests Add unit tests for move ctor and move assignment operator, as well for a few other SGSharedPtr methods. --- simgear/structure/shared_ptr_test.cpp | 119 ++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/simgear/structure/shared_ptr_test.cpp b/simgear/structure/shared_ptr_test.cpp index e99056b3..94f2813e 100644 --- a/simgear/structure/shared_ptr_test.cpp +++ b/simgear/structure/shared_ptr_test.cpp @@ -3,6 +3,8 @@ // Unit tests for reference counting and smart pointer classes #include +#include + #include // EXIT_SUCCESS #include @@ -37,6 +39,7 @@ void test_SGSharedPtr() SG_CHECK_EQUAL( ReferenceCounted::count(ptr.get()), 1 ); SG_CHECK_EQUAL( ptr.getNumRefs(), 1 ); + // Test SGSharedPtr's copy assignment operator RefPtr ptr2 = ptr; SG_CHECK_EQUAL( ptr.getNumRefs(), 2 ); SG_CHECK_EQUAL( ptr2.getNumRefs(), 2 ); @@ -44,6 +47,7 @@ void test_SGSharedPtr() SG_CHECK_EQUAL( ptr, ptr2 ); SG_CHECK_EQUAL( ptr.get(), ptr2.get() ); + // Test SGSharedPtr::reset() with no argument ptr.reset(); SG_CHECK_IS_NULL( ptr.get() ); SG_CHECK_EQUAL( ptr.getNumRefs(), 0 ); @@ -55,6 +59,121 @@ void test_SGSharedPtr() SG_CHECK_EQUAL( ptr.getNumRefs(), 0 ); SG_CHECK_EQUAL( ptr2.getNumRefs(), 0 ); SG_CHECK_EQUAL( instance_count, 0) ; + + // Test operator==() and operator!=() for SGSharedPtr + { + RefPtr ptrA(new ReferenceCounted()); + RefPtr ptrB(ptrA); + RefPtr ptrC(new ReferenceCounted()); + RefPtr emptyPtr{}; + SG_CHECK_EQUAL( ptrA, ptrB ); + SG_CHECK_EQUAL( ptrA.get(), ptrB.get() ); // same thing by definition + SG_CHECK_NE( ptrA, ptrC ); + SG_CHECK_NE( ptrA.get(), ptrC.get() ); + SG_CHECK_NE( ptrB, ptrC ); + SG_CHECK_NE( ptrA, emptyPtr ); + SG_CHECK_EQUAL( emptyPtr, emptyPtr ); + } + + // Test SGSharedPtr::reset(T* p) and SGSharedPtr::operator T*() + { + RefPtr ptrA(new ReferenceCounted()); + SG_CHECK_EQUAL( ptrA.getNumRefs(), 1 ); + + RefPtr ptrB(new ReferenceCounted()); + SG_CHECK_NE( ptrA, ptrB ); + ptrB.reset(ptrA); + SG_CHECK_EQUAL( ptrA, ptrB ); + SG_CHECK_EQUAL( ptrA.getNumRefs(), 2 ); + SG_CHECK_EQUAL( ptrB.getNumRefs(), 2 ); + + RefPtr ptrC(new ReferenceCounted()); + SG_CHECK_NE( ptrA, ptrC ); + SG_CHECK_EQUAL( ptrC.getNumRefs(), 1 ); + // ptrA is implicit converted to ReferenceCounted* + ptrC.reset(ptrA); + SG_CHECK_EQUAL( ptrA.getNumRefs(), 3 ); + SG_CHECK_EQUAL( ptrB.getNumRefs(), 3 ); + SG_CHECK_EQUAL( ptrC.getNumRefs(), 3 ); + SG_CHECK_EQUAL( ptrA, ptrB ); + SG_CHECK_EQUAL( ptrB, ptrC ); + } + + // Test SGSharedPtr's copy constructor + { + RefPtr ptrA(new ReferenceCounted()); + SG_CHECK_EQUAL( ptrA.getNumRefs(), 1 ); + + RefPtr ptrB(ptrA); + SG_CHECK_EQUAL( ptrA.getNumRefs(), 2 ); + SG_CHECK_EQUAL( ptrB.getNumRefs(), 2 ); + SG_CHECK_EQUAL( ptrA, ptrB ); + } + + // Test SGSharedPtr's move constructor + { + RefPtr ptrA(new ReferenceCounted()); + RefPtr ptrB(ptrA); + RefPtr ptrC(std::move(ptrA)); + + SG_CHECK_EQUAL( ptrB.getNumRefs(), 2 ); + SG_CHECK_EQUAL( ptrC.getNumRefs(), 2 ); + SG_CHECK_EQUAL( ptrB, ptrC ); + // Although our implementation has these two properties, they are + // absolutely *not* guaranteed by the C++ move semantics: + SG_CHECK_EQUAL( ptrA.getNumRefs(), 0 ); + SG_CHECK_IS_NULL( ptrA.get() ); + } + + // Test SGSharedPtr's move assignment operator: self-move, supposedly + // undefined behavior but certainly safer as a no-op---which the + // copy-and-swap idiom offers for free. + { + RefPtr ptrA(new ReferenceCounted()); + RefPtr ptrB(ptrA); + + ptrA = std::move(ptrA); + SG_CHECK_EQUAL( ptrA.getNumRefs(), 2 ); + SG_CHECK_EQUAL( ptrB.getNumRefs(), 2 ); + SG_CHECK_IS_NOT_NULL( ptrA.get() ); + SG_CHECK_EQUAL( ptrA, ptrB ); + } + + // Test SGSharedPtr's move assignment operator: move to an empty SGSharedPtr + { + RefPtr ptrA; + RefPtr ptrB(new ReferenceCounted()); + + ptrA = std::move(ptrB); + SG_CHECK_EQUAL( ptrA.getNumRefs(), 1 ); + SG_CHECK_IS_NOT_NULL( ptrA.get() ); + // Implementation detail that is *not* guaranteed by the C++ move + // semantics: + SG_CHECK_EQUAL( ptrB.getNumRefs(), 0 ); + SG_CHECK_IS_NULL( ptrB.get() ); + } + + // Test SGSharedPtr's move assignment operator: move to a non-empty + // SGSharedPtr + { + RefPtr ptrA(new ReferenceCounted()); + RefPtr ptrB(ptrA); + RefPtr ptrC(new ReferenceCounted()); + + SG_CHECK_EQUAL( ptrA.getNumRefs(), 2 ); + SG_CHECK_EQUAL( ptrB.getNumRefs(), 2 ); + SG_CHECK_EQUAL( ptrC.getNumRefs(), 1 ); + SG_CHECK_EQUAL( ptrA, ptrB ); + SG_CHECK_NE( ptrA, ptrC ); + + ptrA = std::move(ptrC); + SG_CHECK_EQUAL( ptrA.getNumRefs(), 1 ); + SG_CHECK_EQUAL( ptrB.getNumRefs(), 1 ); + SG_CHECK_NE( ptrA, ptrB ); + // Implementation detail that is *not* guaranteed by the C++ move + // semantics: + SG_CHECK_IS_NULL( ptrC.get() ); + } } class Base1: From 19dd92d3e0a90b39bff60c8071240d151423c6e5 Mon Sep 17 00:00:00 2001 From: Florent Rougon Date: Sun, 12 Nov 2017 08:22:40 +0100 Subject: [PATCH 17/27] Remove or replace obsolete uses of throw() In C++11, destructors are 'noexcept' by default -> remove useless throw() specifiers. There was one case that wasn't about a destructor: I replaced the 'throw()' with 'noexcept' because this use of 'throw()' is deprecated and 'noexcept' offers the intended meaning as far as I can guess (in C++17, 'throw()' will be equivalent to 'noexcept' anyway). For more info, see: http://en.cppreference.com/w/cpp/language/noexcept_spec https://akrzemi1.wordpress.com/2013/08/20/noexcept-destructors/ --- .../nasal/cppbind/detail/from_nasal_helper.cxx | 2 +- .../nasal/cppbind/detail/from_nasal_helper.hxx | 2 +- simgear/scene/material/EffectBuilder.cxx | 2 +- simgear/scene/material/EffectBuilder.hxx | 2 +- simgear/structure/exception.cxx | 16 ++++++++-------- simgear/structure/exception.hxx | 18 +++++++++--------- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/simgear/nasal/cppbind/detail/from_nasal_helper.cxx b/simgear/nasal/cppbind/detail/from_nasal_helper.cxx index f6a173ea..160d69be 100644 --- a/simgear/nasal/cppbind/detail/from_nasal_helper.cxx +++ b/simgear/nasal/cppbind/detail/from_nasal_helper.cxx @@ -39,7 +39,7 @@ namespace nasal } //---------------------------------------------------------------------------- - bad_nasal_cast::~bad_nasal_cast() throw() + bad_nasal_cast::~bad_nasal_cast() { } diff --git a/simgear/nasal/cppbind/detail/from_nasal_helper.hxx b/simgear/nasal/cppbind/detail/from_nasal_helper.hxx index 86d3c55e..ecfeb2ff 100644 --- a/simgear/nasal/cppbind/detail/from_nasal_helper.hxx +++ b/simgear/nasal/cppbind/detail/from_nasal_helper.hxx @@ -70,7 +70,7 @@ namespace nasal */ explicit bad_nasal_cast(const std::string& msg); - virtual ~bad_nasal_cast() throw(); + virtual ~bad_nasal_cast(); protected: std::string _msg; diff --git a/simgear/scene/material/EffectBuilder.cxx b/simgear/scene/material/EffectBuilder.cxx index 44665942..f6fea4d0 100644 --- a/simgear/scene/material/EffectBuilder.cxx +++ b/simgear/scene/material/EffectBuilder.cxx @@ -85,7 +85,7 @@ BuilderException::BuilderException(const std::string& message, { } -BuilderException::~BuilderException() throw() +BuilderException::~BuilderException() { } diff --git a/simgear/scene/material/EffectBuilder.hxx b/simgear/scene/material/EffectBuilder.hxx index b53272d9..84003109 100644 --- a/simgear/scene/material/EffectBuilder.hxx +++ b/simgear/scene/material/EffectBuilder.hxx @@ -184,7 +184,7 @@ public: BuilderException(); BuilderException(const char* message, const char* origin = 0); BuilderException(const std::string& message, const std::string& = ""); - virtual ~BuilderException() throw(); + virtual ~BuilderException(); }; } diff --git a/simgear/structure/exception.cxx b/simgear/structure/exception.cxx index adfc3f87..3a6d8f32 100644 --- a/simgear/structure/exception.cxx +++ b/simgear/structure/exception.cxx @@ -49,7 +49,7 @@ sg_location::sg_location (const char* path, int line, int column) setPath(path); } -sg_location::~sg_location () throw () +sg_location::~sg_location () { } @@ -144,7 +144,7 @@ sg_throwable::sg_throwable (const char* message, const char* origin) setOrigin(origin); } -sg_throwable::~sg_throwable () throw () +sg_throwable::~sg_throwable () { } @@ -185,7 +185,7 @@ sg_throwable::setOrigin (const char* origin) } } -const char* sg_throwable::what() const throw() +const char* sg_throwable::what() const noexcept { try { return getMessage(); @@ -215,7 +215,7 @@ sg_error::sg_error(const std::string& message, const std::string& origin) { } -sg_error::~sg_error () throw () +sg_error::~sg_error () { } @@ -239,7 +239,7 @@ sg_exception::sg_exception( const std::string& message, { } -sg_exception::~sg_exception () throw () +sg_exception::~sg_exception () { } @@ -279,7 +279,7 @@ sg_io_exception::sg_io_exception( const std::string& message, { } -sg_io_exception::~sg_io_exception () throw () +sg_io_exception::~sg_io_exception () { } @@ -335,7 +335,7 @@ sg_format_exception::sg_format_exception( const std::string& message, setText(text.c_str()); } -sg_format_exception::~sg_format_exception () throw () +sg_format_exception::~sg_format_exception () { } @@ -379,7 +379,7 @@ sg_range_exception::sg_range_exception(const std::string& message, { } -sg_range_exception::~sg_range_exception () throw () +sg_range_exception::~sg_range_exception () { } // end of exception.cxx diff --git a/simgear/structure/exception.hxx b/simgear/structure/exception.hxx index a781894a..e9f6d65c 100644 --- a/simgear/structure/exception.hxx +++ b/simgear/structure/exception.hxx @@ -31,7 +31,7 @@ public: sg_location(const std::string& path, int line = -1, int column = -1); sg_location(const SGPath& path, int line = -1, int column = -1); explicit sg_location(const char* path, int line = -1, int column = -1); - virtual ~sg_location() throw (); + virtual ~sg_location(); virtual const char* getPath() const; virtual void setPath (const char* path); virtual int getLine () const; @@ -58,13 +58,13 @@ public: enum {MAX_TEXT_LEN = 1024}; sg_throwable (); sg_throwable (const char* message, const char* origin = 0); - virtual ~sg_throwable () throw (); + virtual ~sg_throwable (); virtual const char* getMessage () const; virtual const std::string getFormattedMessage () const; virtual void setMessage (const char* message); virtual const char* getOrigin () const; virtual void setOrigin (const char *origin); - virtual const char* what() const throw(); + virtual const char* what() const noexcept; private: char _message[MAX_TEXT_LEN]; char _origin[MAX_TEXT_LEN]; @@ -87,7 +87,7 @@ public: sg_error (); sg_error (const char* message, const char* origin = 0); sg_error (const std::string& message, const std::string& origin = ""); - virtual ~sg_error () throw (); + virtual ~sg_error (); }; @@ -111,7 +111,7 @@ public: sg_exception (); sg_exception (const char* message, const char* origin = 0); sg_exception (const std::string& message, const std::string& = ""); - virtual ~sg_exception () throw (); + virtual ~sg_exception (); }; @@ -136,8 +136,8 @@ public: sg_io_exception (const std::string &message, const std::string &origin = ""); sg_io_exception (const std::string &message, const sg_location &location, const std::string &origin = ""); - - virtual ~sg_io_exception () throw (); + + virtual ~sg_io_exception (); virtual const std::string getFormattedMessage () const; virtual const sg_location &getLocation () const; virtual void setLocation (const sg_location &location); @@ -165,7 +165,7 @@ public: const char* origin = 0); sg_format_exception (const std::string& message, const std::string& text, const std::string& origin = ""); - virtual ~sg_format_exception () throw (); + virtual ~sg_format_exception (); virtual const char* getText () const; virtual void setText (const char* text); private: @@ -190,7 +190,7 @@ public: const char* origin = 0); sg_range_exception (const std::string& message, const std::string& origin = ""); - virtual ~sg_range_exception () throw (); + virtual ~sg_range_exception (); }; #endif From fed449a80165adfcd2d9bf3e9951c8380bc75a0a Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Sun, 12 Nov 2017 17:07:13 +0100 Subject: [PATCH 18/27] Fix error message in axis object animation --- simgear/scene/model/animation.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simgear/scene/model/animation.cxx b/simgear/scene/model/animation.cxx index 3d3302ff..ebe38ca6 100644 --- a/simgear/scene/model/animation.cxx +++ b/simgear/scene/model/animation.cxx @@ -772,7 +772,7 @@ bool SGAnimation::setCenterAndAxisFromObject(osg::Node *rootNode, SGVec3d& cente object_group->setNodeMask(0); } else - SG_LOG(SG_INPUT, SG_ALERT, "Could find a valid line segment for animation: " << axis_object_name); + SG_LOG(SG_INPUT, SG_ALERT, "Could not find a valid line segment for animation: " << axis_object_name); } else if (can_warn) SG_LOG(SG_INPUT, SG_ALERT, "Could not find at least one of the following objects for axis animation: " << axis_object_name); From bd87d3963af0620800604a5f01779d914868c486 Mon Sep 17 00:00:00 2001 From: Thomas Geymayer Date: Sun, 12 Nov 2017 22:39:26 +0100 Subject: [PATCH 19/27] Fix leak in NasalObjectHolder Thanks to Florent Rougon for spotting it. --- simgear/nasal/code.c | 5 +++ simgear/nasal/cppbind/NasalObjectHolder.hxx | 7 ++- simgear/nasal/cppbind/test/nasal_gc_test.cxx | 47 ++++++++++++++++++++ simgear/nasal/nasal.h | 4 ++ 4 files changed, 59 insertions(+), 4 deletions(-) diff --git a/simgear/nasal/code.c b/simgear/nasal/code.c index 2bf46451..49e2e4c6 100644 --- a/simgear/nasal/code.c +++ b/simgear/nasal/code.c @@ -787,6 +787,11 @@ void naGCRelease(int key) naHash_delete(globals->save_hash, naNum(key)); } +int naNumSaved() +{ + return naHash_size(globals->save_hash) + naVec_size(globals->save_hash); +} + void naClearSaved() { naContext c; diff --git a/simgear/nasal/cppbind/NasalObjectHolder.hxx b/simgear/nasal/cppbind/NasalObjectHolder.hxx index 55fb5ba1..9b36bcf7 100644 --- a/simgear/nasal/cppbind/NasalObjectHolder.hxx +++ b/simgear/nasal/cppbind/NasalObjectHolder.hxx @@ -139,10 +139,9 @@ namespace nasal template ObjectHolder::ObjectHolder(naRef obj): _ref(obj), - _gc_key(0) + _gc_key(naIsNil(obj) ? 0 : naGCSave(obj)) { - if( !naIsNil(obj) ) - naGCSave(obj); + } //---------------------------------------------------------------------------- @@ -159,7 +158,7 @@ namespace nasal SGSharedPtr > ObjectHolder::makeShared(naRef obj) { - return SGSharedPtr >( new ObjectHolder(obj) ); + return SGSharedPtr >( new ObjectHolder(obj) ); } } // namespace nasal diff --git a/simgear/nasal/cppbind/test/nasal_gc_test.cxx b/simgear/nasal/cppbind/test/nasal_gc_test.cxx index c1cb6ca3..0d6e2e09 100644 --- a/simgear/nasal/cppbind/test/nasal_gc_test.cxx +++ b/simgear/nasal/cppbind/test/nasal_gc_test.cxx @@ -2,6 +2,9 @@ #include #include "TestContext.hxx" + +#include + #include #include @@ -91,3 +94,47 @@ BOOST_AUTO_TEST_CASE( ghost_gc ) BOOST_REQUIRE(active_instances.empty()); } + +//------------------------------------------------------------------------------ +BOOST_AUTO_TEST_CASE( object_holder_gc ) +{ + TestContext c; + BOOST_REQUIRE_EQUAL(naNumSaved(), 0); + BOOST_REQUIRE(active_instances.empty()); + + //----------------------------------------------- + // Put some ghosts in ObjectHolder and check if + // they are saved from gc + + naRef g1 = createTestGhost(c, 1), + g2 = createTestGhost(c, 2); + + nasal::ObjectHolder<> h1(g1); + BOOST_CHECK_EQUAL(naNumSaved(), 1); + BOOST_CHECK(naIsGhost(h1.get_naRef())); + + nasal::ObjectHolder<> h2(g2); + BOOST_CHECK_EQUAL(naNumSaved(), 2); + BOOST_CHECK(naIsGhost(h2.get_naRef())); + + c.runGC(); + + BOOST_CHECK_EQUAL(active_instances.size(), 2); + BOOST_CHECK_EQUAL(naNumSaved(), 2); + + h1.reset(naNum(1)); + h2.reset(naNum(2)); + BOOST_CHECK_EQUAL(naNumSaved(), 2); + + //----------------------------------------------- + // Check that the saved objects are released + + h1.reset(); + BOOST_CHECK_EQUAL(naNumSaved(), 1); + + h2.reset(); + BOOST_CHECK_EQUAL(naNumSaved(), 0); + + c.runGC(); + BOOST_CHECK_EQUAL(active_instances.size(), 0); +} diff --git a/simgear/nasal/nasal.h b/simgear/nasal/nasal.h index 8b6b2c92..7be9bb4f 100644 --- a/simgear/nasal/nasal.h +++ b/simgear/nasal/nasal.h @@ -58,6 +58,10 @@ int naGCSave(naRef obj); // by the garbage collector. void naGCRelease(int key); +// Get the number of currently saved and not yet again released objects +// (saved by naSave or naGCSave) +int naNumSaved(); + // Drop all saved references void naClearSaved(); From bc3404fcbea75b02531d4da2373ade66eb7c5985 Mon Sep 17 00:00:00 2001 From: Florent Rougon Date: Sun, 12 Nov 2017 23:42:58 +0100 Subject: [PATCH 20/27] SGSharedPtr: more efficient copy and move assignment operators The copy-and-swap idiom is certainly very cute, but often causes unnecessary copies. My commit fedafb9352c2877c5983e90834ffba0fff80ff15 did exactly that, unfortunately. Restore the exact same code for the copy-assignment operator as before commit fedafb935, and add a more efficient implementation for the move-assignment operator. As explained by Howard Hinnant in [1] and [2], if some particular piece of code really needs a strong exception safety guarantee, one can easily add a specific method for that; this is not a valid reason to make the code slower for all other places that have no use for such a guarantee! [1] http://www.slideshare.net/ripplelabs/howard-hinnant-accu2014 [2] https://stackoverflow.com/a/9322542/4756009 --- simgear/structure/SGSharedPtr.hxx | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/simgear/structure/SGSharedPtr.hxx b/simgear/structure/SGSharedPtr.hxx index 181dcbb6..ce242f05 100644 --- a/simgear/structure/SGSharedPtr.hxx +++ b/simgear/structure/SGSharedPtr.hxx @@ -67,10 +67,19 @@ public: ~SGSharedPtr(void) { reset(); } - // This handles copy assignment using the copy-and-swap idiom, and also move - // assignment thanks to the move construtor. - SGSharedPtr& operator=(SGSharedPtr p) - { swap(p); return *this; } + SGSharedPtr& operator=(const SGSharedPtr& p) + { reset(p.get()); return *this; } + + SGSharedPtr& operator=(SGSharedPtr&& p) + { // Whether self-move is to be supported at all is controversial, let's + // take the conservative approach for now + if (this != &p) { + swap(p); + p.reset(); + } + return *this; + } + template SGSharedPtr& operator=(const SGSharedPtr& p) { reset(p.get()); return *this; } From ce7d463710dbf60ad9e19b038c82dd7fccec213a Mon Sep 17 00:00:00 2001 From: Thomas Geymayer Date: Mon, 13 Nov 2017 08:20:47 +0100 Subject: [PATCH 21/27] Fix wrong argument One more catch by Florent Rougon. --- simgear/nasal/code.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simgear/nasal/code.c b/simgear/nasal/code.c index 49e2e4c6..faf534ec 100644 --- a/simgear/nasal/code.c +++ b/simgear/nasal/code.c @@ -789,7 +789,7 @@ void naGCRelease(int key) int naNumSaved() { - return naHash_size(globals->save_hash) + naVec_size(globals->save_hash); + return naHash_size(globals->save_hash) + naVec_size(globals->save); } void naClearSaved() From 6283a515b9994d63245e9ba4c86b339b2cf64e28 Mon Sep 17 00:00:00 2001 From: Florent Rougon Date: Mon, 13 Nov 2017 09:54:48 +0100 Subject: [PATCH 22/27] Fix missing headers in simgear/io/DNSClient.cxx and simgear/props/props_test.cxx is needed for std::sort() and std::find(). --- simgear/io/DNSClient.cxx | 6 +++++- simgear/props/props_test.cxx | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/simgear/io/DNSClient.cxx b/simgear/io/DNSClient.cxx index 07547bc3..080d84ec 100644 --- a/simgear/io/DNSClient.cxx +++ b/simgear/io/DNSClient.cxx @@ -22,9 +22,13 @@ // #include + +#include + #include "DNSClient.hxx" #include -#include +#include + #include namespace simgear { diff --git a/simgear/props/props_test.cxx b/simgear/props/props_test.cxx index 2f61bfbb..14211040 100644 --- a/simgear/props/props_test.cxx +++ b/simgear/props/props_test.cxx @@ -9,6 +9,7 @@ #include +#include #include // std::unique_ptr #include #include From abaaee1af2c8835e55dab940b06b589cdad7c8bf Mon Sep 17 00:00:00 2001 From: Florent Rougon Date: Mon, 13 Nov 2017 11:59:11 +0100 Subject: [PATCH 23/27] Add simgear::noexceptSwap() to simgear/sg_inlines.h This is a function template that is guaranteed to be 'noexcept' as long as compilation succeeds. Idea and implementation from . --- simgear/sg_inlines.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/simgear/sg_inlines.h b/simgear/sg_inlines.h index f10090db..b21304d8 100644 --- a/simgear/sg_inlines.h +++ b/simgear/sg_inlines.h @@ -23,6 +23,7 @@ // // $Id$ +#include #ifndef _SG_INLINES_H #define _SG_INLINES_H @@ -108,5 +109,20 @@ inline void SG_NORMALIZE_RANGE( T &val, const T min, const T max ) { #define SG_DISABLE_COPY(Class) \ Class(const Class &); \ Class &operator=(const Class &); - + +namespace simgear { + +// A swap() that is guaranteed to be 'noexcept' as long as compilation +// succeeds. Idea and implementation from +// . +template +void noexceptSwap(T& a, T& b) noexcept +{ + using std::swap; + static_assert(noexcept(swap(a, b)), "this swap() is not 'noexcept'" ); + swap(a, b); +} + +} // of namespace simgear + #endif // _SG_INLINES_H From a9ec3be2fd845a2f7ebed3031ecc1802d0dc42a3 Mon Sep 17 00:00:00 2001 From: Florent Rougon Date: Mon, 13 Nov 2017 12:26:14 +0100 Subject: [PATCH 24/27] SGSharedPtr: the move constructor and move assignment operator are now 'noexcept' This automatically makes SGSharedPtr more efficient when used in standard containers (among others). See below for the benchmark details. Mark as 'noexcept' (after checking it's legitimate!) the SGSharedPtr and SGReferenced methods required for SGSharedPtr's move constructor and move assignment operator to be guaranteed 'noexcept'. Benchmark --------- I measured a 25 % speedup with g++ 6.3.0 on Linux amd64, CFLAGS=-Wall -O2 as compared to commit 18f04842490dd6c60a9a595f394d97bb4f699a06 (which is just before my changes to SGSharedPtr.hxx) on the following test code, called with: nbIterations = 3000000 minSize = 0 maxSize = 200 ------------------------------------------------------------------------ static std::default_random_engine randomNumbersGenerator; class SGReferencedTestClass : public SGReferenced { int i; }; void SGSharedPtr_perfTest(std::size_t nbIterations, std::size_t minSize, std::size_t maxSize) { using Ref = SGSharedPtr; std::uniform_int_distribution sizeDist(minSize, maxSize); auto randomSize = std::bind(sizeDist, randomNumbersGenerator); std::chrono::time_point start, end; start = std::chrono::system_clock::now(); std::vector v; for (std::size_t i=0; i < nbIterations; i++) { v = std::vector{}; // start anew for (std::size_t j=0; j < randomSize(); j++) { auto p = Ref(new SGReferencedTestClass()); v.emplace_back(std::move(p)); } std::shuffle(v.begin(), v.end(), randomNumbersGenerator); std::sort(v.begin(), v.end()); } end = std::chrono::system_clock::now(); std::chrono::duration elapsedSecs = end - start; std::cout << elapsedSecs.count() << "\n"; // duration in seconds } ------------------------------------------------------------------------ Basically, these gains can be explained by the fact that copying an SGSharedPtr requires to test SGReferenced::ref, increase the refcount, and then when the object is destroyed, test again SGReferenced::ref, decrease the refcount and test it in order to maybe delete. With the move constructor and move assignment operator, copying the argument is never necessary: its raw pointer can be swapped with the one contained in *this, which is very fast. For the move constructor, this is all that is needed; move assignment just needs one reset() call after that in order to release the resource from the moved-from shared pointer. --- simgear/structure/SGReferenced.hxx | 2 +- simgear/structure/SGSharedPtr.hxx | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/simgear/structure/SGReferenced.hxx b/simgear/structure/SGReferenced.hxx index 4c7fabd2..8e11cc66 100644 --- a/simgear/structure/SGReferenced.hxx +++ b/simgear/structure/SGReferenced.hxx @@ -43,7 +43,7 @@ public: static unsigned get(const SGReferenced* ref) { if (ref) return ++(ref->_refcount); else return 0; } - static unsigned put(const SGReferenced* ref) + static unsigned put(const SGReferenced* ref) noexcept { if (ref) return --(ref->_refcount); else return 0; } static unsigned count(const SGReferenced* ref) { if (ref) return ref->_refcount; else return 0; } diff --git a/simgear/structure/SGSharedPtr.hxx b/simgear/structure/SGSharedPtr.hxx index ce242f05..fdc64415 100644 --- a/simgear/structure/SGSharedPtr.hxx +++ b/simgear/structure/SGSharedPtr.hxx @@ -20,8 +20,9 @@ #ifndef SGSharedPtr_HXX #define SGSharedPtr_HXX +#include + #include "SGReferenced.hxx" -#include template class SGWeakPtr; @@ -50,13 +51,15 @@ class SGSharedPtr { public: typedef T element_type; - SGSharedPtr(void) : _ptr(0) + SGSharedPtr(void) noexcept + : _ptr(0) {} SGSharedPtr(T* ptr) : _ptr(ptr) { get(_ptr); } SGSharedPtr(const SGSharedPtr& p) : _ptr(p.get()) { get(_ptr); } - SGSharedPtr(SGSharedPtr&& other) : SGSharedPtr() + SGSharedPtr(SGSharedPtr&& other) noexcept + : SGSharedPtr() { swap(other); } template SGSharedPtr(const SGSharedPtr& p) : _ptr(p.get()) @@ -70,7 +73,7 @@ public: SGSharedPtr& operator=(const SGSharedPtr& p) { reset(p.get()); return *this; } - SGSharedPtr& operator=(SGSharedPtr&& p) + SGSharedPtr& operator=(SGSharedPtr&& p) noexcept { // Whether self-move is to be supported at all is controversial, let's // take the conservative approach for now if (this != &p) { @@ -99,7 +102,7 @@ public: { return _ptr; } T* release() { T* tmp = _ptr; _ptr = 0; T::put(tmp); return tmp; } - void reset() + void reset() noexcept { if (!T::put(_ptr)) delete _ptr; _ptr = 0; } void reset(T* p) { SGSharedPtr(p).swap(*this); } @@ -114,8 +117,8 @@ public: void clear() { reset(); } - void swap(SGSharedPtr& other) - { std::swap(_ptr, other._ptr); } + void swap(SGSharedPtr& other) noexcept + { simgear::noexceptSwap(_ptr, other._ptr); } private: void assignNonRef(T* p) From 0c7cabe46f30272574638f840d6d50f61c566b63 Mon Sep 17 00:00:00 2001 From: Florent Rougon Date: Tue, 14 Nov 2017 01:31:03 +0100 Subject: [PATCH 25/27] SGSharedPtr: optimized version of the free function swap() With this simple change, the speedup as compared to commit 18f048424 is now 37 % for the benchmark given in the previous commit. This is because optimized swap() only needs to swap the raw pointers, which is certainly less work than the three move assignments on SGSharedPtr (not raw pointers) done by std::swap(). To benefit from this, write code like: using std::swap; // now useless for SGSharedPtr, but idiomatic swap(ptr1, ptr2); // *not* std::swap()! --- simgear/structure/SGSharedPtr.hxx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/simgear/structure/SGSharedPtr.hxx b/simgear/structure/SGSharedPtr.hxx index fdc64415..d9f5540d 100644 --- a/simgear/structure/SGSharedPtr.hxx +++ b/simgear/structure/SGSharedPtr.hxx @@ -134,6 +134,12 @@ private: friend class SGWeakPtr; }; +template +void swap(SGSharedPtr& a, SGSharedPtr& b) noexcept +{ + a.swap(b); +} + /** * Support for boost::mem_fn */ From e8648a3f710d35214f44b874cfe2465a731107cb Mon Sep 17 00:00:00 2001 From: Florent Rougon Date: Thu, 16 Nov 2017 00:42:22 +0100 Subject: [PATCH 26/27] Add missing include for std::max() --- simgear/structure/subsystem_mgr.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/simgear/structure/subsystem_mgr.cxx b/simgear/structure/subsystem_mgr.cxx index b01a0ffe..eeda8663 100644 --- a/simgear/structure/subsystem_mgr.cxx +++ b/simgear/structure/subsystem_mgr.cxx @@ -22,6 +22,8 @@ # include #endif +#include + #include #include From 55d75f18de4fe4eb2fe78676df2e4e1d0d069362 Mon Sep 17 00:00:00 2001 From: Florent Rougon Date: Thu, 16 Nov 2017 16:00:14 +0100 Subject: [PATCH 27/27] Add missing include for std::max() and std::min() This include was missing in several files from the simgear/hla directory. --- simgear/hla/HLAArrayDataType.cxx | 2 ++ simgear/hla/HLADataType.cxx | 2 ++ simgear/hla/HLAEnumeratedDataType.cxx | 4 ++-- simgear/hla/HLAFixedRecordDataType.cxx | 3 ++- simgear/hla/HLAInteractionClass.cxx | 6 +++--- simgear/hla/HLAObjectClass.cxx | 2 ++ simgear/hla/HLAVariantRecordDataType.cxx | 3 ++- 7 files changed, 15 insertions(+), 7 deletions(-) diff --git a/simgear/hla/HLAArrayDataType.cxx b/simgear/hla/HLAArrayDataType.cxx index e5302a8b..4037b81e 100644 --- a/simgear/hla/HLAArrayDataType.cxx +++ b/simgear/hla/HLAArrayDataType.cxx @@ -19,6 +19,8 @@ # include #endif +#include + #include #include "HLAArrayDataType.hxx" diff --git a/simgear/hla/HLADataType.cxx b/simgear/hla/HLADataType.cxx index 1eaf0456..e20251fe 100644 --- a/simgear/hla/HLADataType.cxx +++ b/simgear/hla/HLADataType.cxx @@ -19,6 +19,8 @@ # include #endif +#include + #include #include "HLADataType.hxx" diff --git a/simgear/hla/HLAEnumeratedDataType.cxx b/simgear/hla/HLAEnumeratedDataType.cxx index 9edb9bf0..60b684d0 100644 --- a/simgear/hla/HLAEnumeratedDataType.cxx +++ b/simgear/hla/HLAEnumeratedDataType.cxx @@ -21,11 +21,11 @@ #include -#include "HLAEnumeratedDataType.hxx" - +#include #include #include #include + #include "HLAEnumeratedDataType.hxx" #include "HLADataTypeVisitor.hxx" diff --git a/simgear/hla/HLAFixedRecordDataType.cxx b/simgear/hla/HLAFixedRecordDataType.cxx index aa4f1011..078f9312 100644 --- a/simgear/hla/HLAFixedRecordDataType.cxx +++ b/simgear/hla/HLAFixedRecordDataType.cxx @@ -19,10 +19,11 @@ # include #endif +#include + #include #include "HLAFixedRecordDataType.hxx" - #include "HLADataTypeVisitor.hxx" #include "HLAFixedRecordDataElement.hxx" diff --git a/simgear/hla/HLAInteractionClass.cxx b/simgear/hla/HLAInteractionClass.cxx index c5b1cc5e..c72eb279 100644 --- a/simgear/hla/HLAInteractionClass.cxx +++ b/simgear/hla/HLAInteractionClass.cxx @@ -19,12 +19,12 @@ # include #endif +#include + #include - -#include "HLAInteractionClass.hxx" - #include +#include "HLAInteractionClass.hxx" #include "HLADataElement.hxx" #include "HLAFederate.hxx" diff --git a/simgear/hla/HLAObjectClass.cxx b/simgear/hla/HLAObjectClass.cxx index 2740eb02..4a26f6dd 100644 --- a/simgear/hla/HLAObjectClass.cxx +++ b/simgear/hla/HLAObjectClass.cxx @@ -19,6 +19,8 @@ # include #endif +#include + #include #include "HLAObjectClass.hxx" diff --git a/simgear/hla/HLAVariantRecordDataType.cxx b/simgear/hla/HLAVariantRecordDataType.cxx index d5c5d44b..733e0c60 100644 --- a/simgear/hla/HLAVariantRecordDataType.cxx +++ b/simgear/hla/HLAVariantRecordDataType.cxx @@ -19,10 +19,11 @@ # include #endif +#include + #include #include "HLAVariantRecordDataType.hxx" - #include "HLADataTypeVisitor.hxx" #include "HLAVariantRecordDataElement.hxx"