diff --git a/CMakeLists.txt b/CMakeLists.txt index ec9dfbd2..71ba3cc4 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) 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/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 ( 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" 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/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; } diff --git a/simgear/misc/strutils.cxx b/simgear/misc/strutils.cxx index f45e0ac8..e8117e6b 100644 --- a/simgear/misc/strutils.cxx +++ b/simgear/misc/strutils.cxx @@ -29,10 +29,7 @@ #include // strerror_r() and strerror_s() #include #include - -#if defined(HAVE_CPP11_CODECVT) - #include // new in C++11 -#endif +#include #include "strutils.hxx" @@ -44,6 +41,8 @@ #if defined(SG_WINDOWS) #include + #include + #include #endif using std::string; @@ -656,25 +655,85 @@ 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) +#if defined(SG_WINDOWS) std::wstring_convert, wchar_t> ucs2conv; return ucs2conv.from_bytes(a); #else - return std::wstring(); + 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) { -#ifdef SG_WINDOWS - return convertWStringToMultiByte(CP_UTF8, w); -#elif defined(HAVE_CPP11_CODECVT) +#if defined(SG_WINDOWS) std::wstring_convert, wchar_t> ucs2conv; return ucs2conv.to_bytes(w); #else - return std::string(); + 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 } 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; } diff --git a/simgear/nasal/code.c b/simgear/nasal/code.c index 2bf46451..faf534ec 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); +} + 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/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/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(); 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. */ 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; 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 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/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..ebe38ca6 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); @@ -769,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); 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 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/filters.cxx b/simgear/sound/filters.cxx new file mode 100644 index 00000000..b8539c96 --- /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 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 + * 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..4e153c4a --- /dev/null +++ b/simgear/sound/filters.hxx @@ -0,0 +1,77 @@ +/* + * Copyright 2007-2017 by Erik Hofman. + * Copyright 2009-2017 by Adalin B.V. + * + * 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 + * 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 + +#include + +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 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 ); } 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/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 3afcfc91..d9f5540d 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,12 +51,16 @@ 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) noexcept + : SGSharedPtr() + { swap(other); } template SGSharedPtr(const SGSharedPtr& p) : _ptr(p.get()) { get(_ptr); } @@ -64,9 +69,20 @@ public: { reset(p.lock().get()); } ~SGSharedPtr(void) { reset(); } - + SGSharedPtr& operator=(const SGSharedPtr& p) { reset(p.get()); return *this; } + + 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) { + swap(p); + p.reset(); + } + return *this; + } + template SGSharedPtr& operator=(const SGSharedPtr& p) { reset(p.get()); return *this; } @@ -86,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); } @@ -101,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) @@ -118,6 +134,12 @@ private: friend class SGWeakPtr; }; +template +void swap(SGSharedPtr& a, SGSharedPtr& b) noexcept +{ + a.swap(b); +} + /** * Support for boost::mem_fn */ 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 diff --git a/simgear/structure/shared_ptr_test.cpp b/simgear/structure/shared_ptr_test.cpp index 877e1468..94f2813e 100644 --- a/simgear/structure/shared_ptr_test.cpp +++ b/simgear/structure/shared_ptr_test.cpp @@ -1,6 +1,13 @@ -/// 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 + +#include // EXIT_SUCCESS + +#include #include "SGSharedPtr.hxx" #include "SGWeakPtr.hxx" @@ -21,33 +28,152 @@ 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 ); + // Test SGSharedPtr's copy assignment operator 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() ); + // Test SGSharedPtr::reset() with no argument 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) ; + + // 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: @@ -63,31 +189,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; } 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