Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/simgear into next

This commit is contained in:
gallaert 2017-11-17 18:46:21 +00:00
commit 12bed8e8ad
45 changed files with 974 additions and 165 deletions

View File

@ -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 # isnan might not be real symbol, so can't check using function_exists
check_cxx_source_compiles( check_cxx_source_compiles(
"#include <cstdlib> "#include <cmath>
int main() { return std::isnan(0.0);} " int main() { return std::isnan(0.0);} "
HAVE_STD_ISNAN) HAVE_STD_ISNAN)

View File

@ -10,12 +10,13 @@
# #
# Created by Erik Hofman. # Created by Erik Hofman.
FIND_PATH(AAX_INCLUDE_DIR aax/aeonwave.hpp FIND_PATH(AAX_INCLUDE_DIR aax/aax.h
HINTS HINTS
$ENV{AAXDIR} $ENV{AAXDIR}
$ENV{ProgramFiles}/aax $ENV{ProgramFiles}/aax
$ENV{ProgramFiles}/AeonWave $ENV{ProgramFiles}/AeonWave
$ENV{ProgramFiles}/Adalin/AeonWave $ENV{ProgramFiles}/Adalin/AeonWave
${CMAKE_SOURCE_DIR}/aax
PATH_SUFFIXES include PATH_SUFFIXES include
PATHS PATHS
~/Library/Frameworks ~/Library/Frameworks
@ -26,23 +27,35 @@ FIND_PATH(AAX_INCLUDE_DIR aax/aeonwave.hpp
) )
FIND_LIBRARY(AAX_LIBRARY FIND_LIBRARY(AAX_LIBRARY
NAMES AAX aax AAX32 libAAX32 NAMES AAX aax AAX32
HINTS HINTS
$ENV{AAXDIR} $ENV{AAXDIR}
$ENV{ProgramFiles}/AAX $ENV{ProgramFiles}/AAX
$ENV{ProgramFiles}/AeonWave $ENV{ProgramFiles}/AeonWave
$ENV{ProgramFiles}/Adalin/AeonWave $ENV{ProgramFiles}/Adalin/AeonWave
${CMAKE_BUILD_DIR}/aax
PATH_SUFFIXES bin lib lib/${CMAKE_LIBRARY_ARCHITECTURE} lib64 libs64 libs libs/Win32 libs/Win64 PATH_SUFFIXES bin lib lib/${CMAKE_LIBRARY_ARCHITECTURE} lib64 libs64 libs libs/Win32 libs/Win64
PATHS PATHS
~/Library/Frameworks ~/Library/Frameworks
/Library/Frameworks /Library/Frameworks
/usr/local
/usr /usr
/opt /opt
/usr/local
) )
SET(AAX_FOUND "NO")
IF(AAX_LIBRARY AND AAX_INCLUDE_DIR) IF(AAX_LIBRARY AND AAX_INCLUDE_DIR)
SET(AAX_FOUND "YES") 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) ENDIF(AAX_LIBRARY AND AAX_INCLUDE_DIR)

View File

@ -7,6 +7,7 @@ find_dependency(Threads)
set(SIMGEAR_HEADLESS @SIMGEAR_HEADLESS@) set(SIMGEAR_HEADLESS @SIMGEAR_HEADLESS@)
set(SIMGEAR_SOUND @ENABLE_SOUND@) set(SIMGEAR_SOUND @ENABLE_SOUND@)
set(USE_AEONWAVE @USE_AEONWAVE@)
# OpenAL isn't a public dependency, so maybe not needed # OpenAL isn't a public dependency, so maybe not needed
#if (SIMGEAR_SOUND) #if (SIMGEAR_SOUND)

View File

@ -433,6 +433,7 @@ namespace canvas
osg::Vec2f Element::posToLocal(const osg::Vec2f& pos) const osg::Vec2f Element::posToLocal(const osg::Vec2f& pos) const
{ {
getMatrix(); getMatrix();
if (! _transform) return osg::Vec2f(pos[0], pos[1]);
const osg::Matrix& m = _transform->getInverseMatrix(); const osg::Matrix& m = _transform->getInverseMatrix();
return osg::Vec2f return osg::Vec2f
( (

View File

@ -19,6 +19,8 @@
# include <simgear_config.h> # include <simgear_config.h>
#endif #endif
#include <algorithm>
#include <simgear/compiler.h> #include <simgear/compiler.h>
#include "HLAArrayDataType.hxx" #include "HLAArrayDataType.hxx"

View File

@ -19,6 +19,8 @@
# include <simgear_config.h> # include <simgear_config.h>
#endif #endif
#include <algorithm>
#include <simgear/compiler.h> #include <simgear/compiler.h>
#include "HLADataType.hxx" #include "HLADataType.hxx"

View File

@ -21,11 +21,11 @@
#include <simgear/compiler.h> #include <simgear/compiler.h>
#include "HLAEnumeratedDataType.hxx" #include <algorithm>
#include <map> #include <map>
#include <sstream> #include <sstream>
#include <vector> #include <vector>
#include "HLAEnumeratedDataType.hxx" #include "HLAEnumeratedDataType.hxx"
#include "HLADataTypeVisitor.hxx" #include "HLADataTypeVisitor.hxx"

View File

@ -19,10 +19,11 @@
# include <simgear_config.h> # include <simgear_config.h>
#endif #endif
#include <algorithm>
#include <simgear/compiler.h> #include <simgear/compiler.h>
#include "HLAFixedRecordDataType.hxx" #include "HLAFixedRecordDataType.hxx"
#include "HLADataTypeVisitor.hxx" #include "HLADataTypeVisitor.hxx"
#include "HLAFixedRecordDataElement.hxx" #include "HLAFixedRecordDataElement.hxx"

View File

@ -19,12 +19,12 @@
# include <simgear_config.h> # include <simgear_config.h>
#endif #endif
#include <algorithm>
#include <simgear/compiler.h> #include <simgear/compiler.h>
#include "HLAInteractionClass.hxx"
#include <simgear/debug/logstream.hxx> #include <simgear/debug/logstream.hxx>
#include "HLAInteractionClass.hxx"
#include "HLADataElement.hxx" #include "HLADataElement.hxx"
#include "HLAFederate.hxx" #include "HLAFederate.hxx"

View File

@ -19,6 +19,8 @@
# include <simgear_config.h> # include <simgear_config.h>
#endif #endif
#include <algorithm>
#include <simgear/compiler.h> #include <simgear/compiler.h>
#include "HLAObjectClass.hxx" #include "HLAObjectClass.hxx"

View File

@ -19,10 +19,11 @@
# include <simgear_config.h> # include <simgear_config.h>
#endif #endif
#include <algorithm>
#include <simgear/compiler.h> #include <simgear/compiler.h>
#include "HLAVariantRecordDataType.hxx" #include "HLAVariantRecordDataType.hxx"
#include "HLADataTypeVisitor.hxx" #include "HLADataTypeVisitor.hxx"
#include "HLAVariantRecordDataElement.hxx" #include "HLAVariantRecordDataElement.hxx"

View File

@ -22,9 +22,13 @@
// //
#include <simgear_config.h> #include <simgear_config.h>
#include <algorithm>
#include "DNSClient.hxx" #include "DNSClient.hxx"
#include <udns.h> #include <udns.h>
#include <time.h> #include <ctime>
#include <simgear/debug/logstream.hxx> #include <simgear/debug/logstream.hxx>
namespace simgear { namespace simgear {

View File

@ -296,7 +296,10 @@ bool Dir::isEmpty() const
int n = 0; int n = 0;
dirent* d; dirent* d;
while( (d = readdir(dp)) !=NULL && (n < 4) ) n++; while (n < 3 && (d = readdir(dp)) != nullptr) {
n++;
}
closedir(dp); closedir(dp);
return (n == 2); // '.' and '..' always exist return (n == 2); // '.' and '..' always exist

View File

@ -2,6 +2,7 @@
#include <cstdlib> #include <cstdlib>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/misc/sg_path.hxx> #include <simgear/misc/sg_path.hxx>
#include <simgear/misc/test_macros.hxx> #include <simgear/misc/test_macros.hxx>
#include "sg_dir.hxx" #include "sg_dir.hxx"
@ -34,11 +35,35 @@ void test_tempDir()
d.remove(); 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) int main(int argc, char **argv)
{ {
test_isNull(); test_isNull();
test_setRemoveOnDestroy(); test_setRemoveOnDestroy();
test_tempDir(); test_tempDir();
test_isEmpty();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -29,10 +29,7 @@
#include <cstring> // strerror_r() and strerror_s() #include <cstring> // strerror_r() and strerror_s()
#include <cctype> #include <cctype>
#include <cerrno> #include <cerrno>
#include <cassert>
#if defined(HAVE_CPP11_CODECVT)
#include <codecvt> // new in C++11
#endif
#include "strutils.hxx" #include "strutils.hxx"
@ -44,6 +41,8 @@
#if defined(SG_WINDOWS) #if defined(SG_WINDOWS)
#include <windows.h> #include <windows.h>
#include <codecvt>
#include <locale>
#endif #endif
using std::string; using std::string;
@ -656,25 +655,85 @@ static std::string convertWStringToMultiByte(DWORD encoding, const std::wstring&
std::wstring convertUtf8ToWString(const std::string& a) std::wstring convertUtf8ToWString(const std::string& a)
{ {
#ifdef SG_WINDOWS #if defined(SG_WINDOWS)
return convertMultiByteToWString(CP_UTF8, a);
#elif defined(HAVE_CPP11_CODECVT)
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> ucs2conv; std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> ucs2conv;
return ucs2conv.from_bytes(a); return ucs2conv.from_bytes(a);
#else #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<wchar_t>(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 #endif
} }
std::string convertWStringToUtf8(const std::wstring& w) std::string convertWStringToUtf8(const std::wstring& w)
{ {
#ifdef SG_WINDOWS #if defined(SG_WINDOWS)
return convertWStringToMultiByte(CP_UTF8, w);
#elif defined(HAVE_CPP11_CODECVT)
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> ucs2conv; std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> ucs2conv;
return ucs2conv.to_bytes(w); return ucs2conv.to_bytes(w);
#else #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<uint8_t>(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 #endif
} }

View File

@ -605,6 +605,20 @@ void test_readTime()
SG_CHECK_EQUAL_EP(strutils::readTime("-0:0:28"), -28 * seconds); 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[]) int main(int argc, char* argv[])
{ {
test_strip(); test_strip();
@ -624,6 +638,7 @@ int main(int argc, char* argv[])
test_error_string(); test_error_string();
test_propPathMatch(); test_propPathMatch();
test_readTime(); test_readTime();
test_utf8Convert();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -787,6 +787,11 @@ void naGCRelease(int key)
naHash_delete(globals->save_hash, naNum(key)); naHash_delete(globals->save_hash, naNum(key));
} }
int naNumSaved()
{
return naHash_size(globals->save_hash) + naVec_size(globals->save);
}
void naClearSaved() void naClearSaved()
{ {
naContext c; naContext c;

View File

@ -139,10 +139,9 @@ namespace nasal
template<class Base> template<class Base>
ObjectHolder<Base>::ObjectHolder(naRef obj): ObjectHolder<Base>::ObjectHolder(naRef obj):
_ref(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<Base> > SGSharedPtr<ObjectHolder<Base> >
ObjectHolder<Base>::makeShared(naRef obj) ObjectHolder<Base>::makeShared(naRef obj)
{ {
return SGSharedPtr<ObjectHolder<Base> >( new ObjectHolder<SGReferenced>(obj) ); return SGSharedPtr<ObjectHolder<Base> >( new ObjectHolder<Base>(obj) );
} }
} // namespace nasal } // namespace nasal

View File

@ -39,7 +39,7 @@ namespace nasal
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
bad_nasal_cast::~bad_nasal_cast() throw() bad_nasal_cast::~bad_nasal_cast()
{ {
} }

View File

@ -70,7 +70,7 @@ namespace nasal
*/ */
explicit bad_nasal_cast(const std::string& msg); explicit bad_nasal_cast(const std::string& msg);
virtual ~bad_nasal_cast() throw(); virtual ~bad_nasal_cast();
protected: protected:
std::string _msg; std::string _msg;

View File

@ -2,6 +2,9 @@
#include <BoostTestTargetConfig.h> #include <BoostTestTargetConfig.h>
#include "TestContext.hxx" #include "TestContext.hxx"
#include <simgear/nasal/cppbind/NasalObjectHolder.hxx>
#include <iostream> #include <iostream>
#include <set> #include <set>
@ -91,3 +94,47 @@ BOOST_AUTO_TEST_CASE( ghost_gc )
BOOST_REQUIRE(active_instances.empty()); 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);
}

View File

@ -58,6 +58,10 @@ int naGCSave(naRef obj);
// by the garbage collector. // by the garbage collector.
void naGCRelease(int key); 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 // Drop all saved references
void naClearSaved(); void naClearSaved();

View File

@ -2443,6 +2443,7 @@ SGPropertyNode::fireValueChanged (SGPropertyNode * node)
{ {
if (_listeners != 0) { if (_listeners != 0) {
for (unsigned int i = 0; i < _listeners->size(); i++) { for (unsigned int i = 0; i < _listeners->size(); i++) {
if ((*_listeners)[i])
(*_listeners)[i]->valueChanged(node); (*_listeners)[i]->valueChanged(node);
} }
} }
@ -2630,6 +2631,30 @@ bool compareNodeValue(const SGPropertyNode& lhs, const SGPropertyNode& rhs)
} }
} }
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, bool SGPropertyNode::compare(const SGPropertyNode& lhs,
const SGPropertyNode& rhs) const SGPropertyNode& rhs)
{ {

View File

@ -1105,6 +1105,11 @@ public:
*/ */
SGPropertyNode * getNode (const char * relative_path, bool create = false); 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. * Get a pointer to another node by relative path.
*/ */

View File

@ -161,8 +161,7 @@ setFlag( int& mode,
string message = "Unrecognized flag value '"; string message = "Unrecognized flag value '";
message += flag; message += flag;
message += '\''; message += '\'';
// FIXME: add location info throw sg_io_exception(message, location, SG_ORIGIN);
throw sg_io_exception(message, location, "SimGear Property Reader");
} }
} }
@ -177,7 +176,7 @@ PropsVisitor::startElement (const char * name, const XMLAttributes &atts)
string message = "Root element name is "; string message = "Root element name is ";
message += name; message += name;
message += "; expected PropertyList"; message += "; expected PropertyList";
throw sg_io_exception(message, location, "SimGear Property Reader"); throw sg_io_exception(message, location, SG_ORIGIN);
} }
// Check for an include. // Check for an include.
@ -189,8 +188,7 @@ PropsVisitor::startElement (const char * name, const XMLAttributes &atts)
{ {
string message ="Cannot open file "; string message ="Cannot open file ";
message += attval; message += attval;
throw sg_io_exception(message, location, throw sg_io_exception(message, location, SG_ORIGIN);
"SimGear Property Reader");
} }
readProperties(path, _root, 0, _extended); readProperties(path, _root, 0, _extended);
} catch (sg_io_exception &e) { } catch (sg_io_exception &e) {
@ -277,7 +275,7 @@ PropsVisitor::startElement (const char * name, const XMLAttributes &atts)
{ {
string message ="Cannot open file "; string message ="Cannot open file ";
message += val; message += val;
throw sg_io_exception(message, location, "SimGear Property Reader"); throw sg_io_exception(message, location, SG_ORIGIN);
} }
readProperties(path, node, 0, _extended); readProperties(path, node, 0, _extended);
} }
@ -352,8 +350,7 @@ PropsVisitor::endElement (const char * name)
string message = "Unrecognized data type '"; string message = "Unrecognized data type '";
message += st.type; message += st.type;
message += '\''; message += '\'';
// FIXME: add location information throw sg_io_exception(message, location, SG_ORIGIN);
throw sg_io_exception(message, location, "SimGear Property Reader");
} }
if( !ret ) if( !ret )
SG_LOG SG_LOG
@ -770,7 +767,7 @@ copyPropertyValue(const SGPropertyNode *in, SGPropertyNode *out)
break; break;
string message = "Unknown internal SGPropertyNode type"; string message = "Unknown internal SGPropertyNode type";
message += in->getType(); message += in->getType();
throw sg_error(message, "SimGear Property Reader"); throw sg_error(message, SG_ORIGIN);
} }
return retval; return retval;

View File

@ -9,6 +9,7 @@
#include <simgear/compiler.h> #include <simgear/compiler.h>
#include <algorithm>
#include <memory> // std::unique_ptr #include <memory> // std::unique_ptr
#include <iostream> #include <iostream>
#include <map> #include <map>

View File

@ -85,7 +85,7 @@ BuilderException::BuilderException(const std::string& message,
{ {
} }
BuilderException::~BuilderException() throw() BuilderException::~BuilderException()
{ {
} }

View File

@ -184,7 +184,7 @@ public:
BuilderException(); BuilderException();
BuilderException(const char* message, const char* origin = 0); BuilderException(const char* message, const char* origin = 0);
BuilderException(const std::string& message, const std::string& = ""); BuilderException(const std::string& message, const std::string& = "");
virtual ~BuilderException() throw(); virtual ~BuilderException();
}; };
} }

View File

@ -832,3 +832,157 @@ SGSliderAnimation::setupCallbacks(SGSceneUserData* ud, osg::Group*)
{ {
ud->setPickCallback(new KnobSliderPickCallback(getConfig(), getModelRoot())); 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<SGPropertyNode_ptr> 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<int> _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<SGPropertyNode_ptr> 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()));
}

View File

@ -114,5 +114,19 @@ private:
SGSharedPtr<SGExpressiond const> _animationValue; SGSharedPtr<SGExpressiond const> _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 #endif // of SG_SCENE_PICK_ANIMATION_HXX

View File

@ -226,7 +226,7 @@ namespace {
{ {
string typeString(aNode->getStringValue("type")); string typeString(aNode->getStringValue("type"));
// exclude these so we don't show yellow outlines in preview mode // 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");
} }
}; };

View File

@ -497,6 +497,9 @@ SGAnimation::animate(simgear::SGTransientModelData &modelData)
} else if (type == "slider") { } else if (type == "slider") {
SGSliderAnimation anim(modelData); SGSliderAnimation anim(modelData);
anim.apply(modelData.getNode()); anim.apply(modelData.getNode());
} else if (type == "touch") {
SGTouchAnimation anim(modelData);
anim.apply(modelData.getNode());
} else if (type == "range") { } else if (type == "range") {
SGRangeAnimation anim(modelData); SGRangeAnimation anim(modelData);
anim.apply(modelData); anim.apply(modelData);
@ -769,7 +772,7 @@ bool SGAnimation::setCenterAndAxisFromObject(osg::Node *rootNode, SGVec3d& cente
object_group->setNodeMask(0); object_group->setNodeMask(0);
} }
else 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) 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); SG_LOG(SG_INPUT, SG_ALERT, "Could not find at least one of the following objects for axis animation: " << axis_object_name);

View File

@ -23,6 +23,7 @@
// //
// $Id$ // $Id$
#include <utility>
#ifndef _SG_INLINES_H #ifndef _SG_INLINES_H
#define _SG_INLINES_H #define _SG_INLINES_H
@ -109,4 +110,19 @@ inline void SG_NORMALIZE_RANGE( T &val, const T min, const T max ) {
Class(const Class &); \ Class(const Class &); \
Class &operator=(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
// <https://akrzemi1.wordpress.com/2011/06/10/using-noexcept/>.
template<typename T>
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 #endif // _SG_INLINES_H

View File

@ -4,16 +4,15 @@ set(HEADERS
sample.hxx sample.hxx
sample_group.hxx sample_group.hxx
xmlsound.hxx xmlsound.hxx
readwav.hxx
soundmgr.hxx soundmgr.hxx
filters.hxx
) )
set(SOURCES set(SOURCES
sample.cxx sample.cxx
sample_group.cxx sample_group.cxx
xmlsound.cxx xmlsound.cxx
readwav.cxx filters.cxx
soundmgr_openal_private.hxx
) )
if (USE_AEONWAVE) if (USE_AEONWAVE)
@ -21,8 +20,13 @@ if (USE_AEONWAVE)
soundmgr_aeonwave.cxx soundmgr_aeonwave.cxx
) )
else() else()
set(HEADERS ${HEADERS}
readwav.hxx
)
set(SOURCES ${SOURCES} set(SOURCES ${SOURCES}
soundmgr_openal.cxx soundmgr_openal.cxx
soundmgr_openal_private.hxx
readwav.cxx
) )
endif() endif()

View File

@ -66,15 +66,15 @@ int main( int argc, char *argv[] )
std::cout << "Vendor: " << _vendor << std::endl; std::cout << "Vendor: " << _vendor << std::endl;
std::cout << "Renderer: " << _renderer << std::endl; std::cout << "Renderer: " << _renderer << std::endl;
aax::Matrix64 mtx64; aax::Matrix mtx;
mtx64.translate(-5000.0, 12500.0, 1000.0); mtx.translate(-5000.0, 12500.0, 1000.0);
aax::Matrix mtx = mtx64.toMatrix(); aax::Matrix64 mtx64 = mtx.toMatrix64();
emitter.matrix(mtx); emitter.matrix(mtx64);
mtx.translate(-5.0, 2.0, 1.0); mtx.translate(-5.0, 2.0, 1.0);
mtx.inverse(); mtx64.inverse();
aax.sensor_matrix(mtx); aax.sensor_matrix(mtx64);
aax.set(AAX_PLAYING); aax.set(AAX_PLAYING);
emitter.set(AAX_PLAYING); emitter.set(AAX_PLAYING);

169
simgear/sound/filters.cxx Normal file
View File

@ -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 <math.h>
#include <stdio.h>
#include <simgear/constants.h>
#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

77
simgear/sound/filters.hxx Normal file
View File

@ -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 <cstdint>
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

View File

@ -92,14 +92,14 @@ public:
} }
void init() { void init() {
_mtx = aax::Matrix(); _mtx = aax::Matrix64();
} }
void update_pos_and_orientation() void update_pos_and_orientation()
{ {
SGVec3d sgv_at = _orientation.backTransform(-SGVec3d::e3()); SGVec3d sgv_at = _orientation.backTransform(-SGVec3d::e3());
SGVec3d sgv_up = _orientation.backTransform(SGVec3d::e2()); 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()); _mtx.set(pos.data(), toVec3f(sgv_at).data(), toVec3f(sgv_up).data());
@ -107,7 +107,7 @@ public:
} }
aax::AeonWave _aax; aax::AeonWave _aax;
aax::Matrix _mtx; aax::Matrix64 _mtx;
SGVec3d _absolute_pos; SGVec3d _absolute_pos;
SGVec3d _base_pos; SGVec3d _base_pos;
@ -338,7 +338,7 @@ void SGSoundMgr::update( double dt )
TRY( dsp.set(AAX_SOUND_VELOCITY, 340.3f) ); TRY( dsp.set(AAX_SOUND_VELOCITY, 340.3f) );
TRY( d->_aax.set(dsp) ); TRY( d->_aax.set(dsp) );
#endif #endif
aax::Matrix mtx = d->_mtx; aax::Matrix64 mtx = d->_mtx;
mtx.inverse(); mtx.inverse();
TRY( d->_aax.sensor_matrix(mtx) ); TRY( d->_aax.sensor_matrix(mtx) );
@ -465,7 +465,7 @@ unsigned int SGSoundMgr::request_buffer(SGSoundSample *sample)
} }
bufid = d->_buffer_id++; bufid = d->_buffer_id++;
d->_buffers.insert( std::make_pair<unsigned int,aax::Buffer&>(bufid,buf) ); d->_buffers.insert( {bufid, buf} );
if ( !sample->is_file() ) { if ( !sample->is_file() ) {
enum aaxFormat format = AAX_FORMAT_NONE; 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_samples = sample->get_no_samples();
unsigned int no_tracks = sample->get_no_tracks(); unsigned int no_tracks = sample->get_no_tracks();
unsigned int frequency = sample->get_frequency(); 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.set(AAX_FREQUENCY, frequency) );
TRY( buf.fill(sample->get_data()) ); 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::Emitter& emitter = d->get_emitter(sample->get_source());
aax::dsp dsp; aax::dsp dsp;
aax::Vector pos = toVec3f(position).data(); aax::Vector64 pos = position.data();
aax::Vector ori = orientation.data(); aax::Vector ori = orientation.data();
aax::Vector vel = velocity.data(); aax::Vector vel = velocity.data();
aax::Matrix mtx(pos, ori); aax::Matrix64 mtx(pos, ori);
TRY( emitter.matrix(mtx) ); TRY( emitter.matrix(mtx) );
TRY( emitter.velocity(vel) ); TRY( emitter.velocity(vel) );
@ -763,7 +763,8 @@ void SGSoundMgr::set_position( const SGVec3d& pos, const SGGeod& pos_geod )
SGVec3f SGSoundMgr::get_direction() const SGVec3f SGSoundMgr::get_direction() const
{ {
aaxVec3f pos, at, up; aaxVec3f at, up;
aaxVec3d pos;
d->_mtx.get(pos, at, up); d->_mtx.get(pos, at, up);
return SGVec3f( at ); return SGVec3f( at );
} }

View File

@ -60,14 +60,13 @@ add_executable(test_expressions expression_test.cxx)
target_link_libraries(test_expressions ${TEST_LIBS}) target_link_libraries(test_expressions ${TEST_LIBS})
add_test(expressions ${EXECUTABLE_OUTPUT_PATH}/test_expressions) 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) endif(ENABLE_TESTS)
add_boost_test(function_list add_boost_test(function_list
SOURCES function_list_test.cxx SOURCES function_list_test.cxx
LIBRARIES ${TEST_LIBS} LIBRARIES ${TEST_LIBS}
) )
add_boost_test(shared_ptr
SOURCES shared_ptr_test.cpp
LIBRARIES ${TEST_LIBS}
)

View File

@ -43,7 +43,7 @@ public:
static unsigned get(const SGReferenced* ref) static unsigned get(const SGReferenced* ref)
{ if (ref) return ++(ref->_refcount); else return 0; } { 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; } { if (ref) return --(ref->_refcount); else return 0; }
static unsigned count(const SGReferenced* ref) static unsigned count(const SGReferenced* ref)
{ if (ref) return ref->_refcount; else return 0; } { if (ref) return ref->_refcount; else return 0; }

View File

@ -20,8 +20,9 @@
#ifndef SGSharedPtr_HXX #ifndef SGSharedPtr_HXX
#define SGSharedPtr_HXX #define SGSharedPtr_HXX
#include <simgear/sg_inlines.h>
#include "SGReferenced.hxx" #include "SGReferenced.hxx"
#include <algorithm>
template<typename T> template<typename T>
class SGWeakPtr; class SGWeakPtr;
@ -50,12 +51,16 @@ class SGSharedPtr {
public: public:
typedef T element_type; typedef T element_type;
SGSharedPtr(void) : _ptr(0) SGSharedPtr(void) noexcept
: _ptr(0)
{} {}
SGSharedPtr(T* ptr) : _ptr(ptr) SGSharedPtr(T* ptr) : _ptr(ptr)
{ get(_ptr); } { get(_ptr); }
SGSharedPtr(const SGSharedPtr& p) : _ptr(p.get()) SGSharedPtr(const SGSharedPtr& p) : _ptr(p.get())
{ get(_ptr); } { get(_ptr); }
SGSharedPtr(SGSharedPtr&& other) noexcept
: SGSharedPtr()
{ swap(other); }
template<typename U> template<typename U>
SGSharedPtr(const SGSharedPtr<U>& p) : _ptr(p.get()) SGSharedPtr(const SGSharedPtr<U>& p) : _ptr(p.get())
{ get(_ptr); } { get(_ptr); }
@ -67,6 +72,17 @@ public:
SGSharedPtr& operator=(const SGSharedPtr& p) SGSharedPtr& operator=(const SGSharedPtr& p)
{ reset(p.get()); return *this; } { 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<typename U> template<typename U>
SGSharedPtr& operator=(const SGSharedPtr<U>& p) SGSharedPtr& operator=(const SGSharedPtr<U>& p)
{ reset(p.get()); return *this; } { reset(p.get()); return *this; }
@ -86,7 +102,7 @@ public:
{ return _ptr; } { return _ptr; }
T* release() T* release()
{ T* tmp = _ptr; _ptr = 0; T::put(tmp); return tmp; } { T* tmp = _ptr; _ptr = 0; T::put(tmp); return tmp; }
void reset() void reset() noexcept
{ if (!T::put(_ptr)) delete _ptr; _ptr = 0; } { if (!T::put(_ptr)) delete _ptr; _ptr = 0; }
void reset(T* p) void reset(T* p)
{ SGSharedPtr(p).swap(*this); } { SGSharedPtr(p).swap(*this); }
@ -101,8 +117,8 @@ public:
void clear() void clear()
{ reset(); } { reset(); }
void swap(SGSharedPtr& other) void swap(SGSharedPtr& other) noexcept
{ std::swap(_ptr, other._ptr); } { simgear::noexceptSwap(_ptr, other._ptr); }
private: private:
void assignNonRef(T* p) void assignNonRef(T* p)
@ -118,6 +134,12 @@ private:
friend class SGWeakPtr; friend class SGWeakPtr;
}; };
template<class T>
void swap(SGSharedPtr<T>& a, SGSharedPtr<T>& b) noexcept
{
a.swap(b);
}
/** /**
* Support for boost::mem_fn * Support for boost::mem_fn
*/ */

View File

@ -49,7 +49,7 @@ sg_location::sg_location (const char* path, int line, int column)
setPath(path); 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); 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 { try {
return getMessage(); 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()); 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 // end of exception.cxx

View File

@ -31,7 +31,7 @@ public:
sg_location(const std::string& path, int line = -1, int column = -1); sg_location(const std::string& path, int line = -1, int column = -1);
sg_location(const SGPath& 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); 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 const char* getPath() const;
virtual void setPath (const char* path); virtual void setPath (const char* path);
virtual int getLine () const; virtual int getLine () const;
@ -58,13 +58,13 @@ public:
enum {MAX_TEXT_LEN = 1024}; enum {MAX_TEXT_LEN = 1024};
sg_throwable (); sg_throwable ();
sg_throwable (const char* message, const char* origin = 0); sg_throwable (const char* message, const char* origin = 0);
virtual ~sg_throwable () throw (); virtual ~sg_throwable ();
virtual const char* getMessage () const; virtual const char* getMessage () const;
virtual const std::string getFormattedMessage () const; virtual const std::string getFormattedMessage () const;
virtual void setMessage (const char* message); virtual void setMessage (const char* message);
virtual const char* getOrigin () const; virtual const char* getOrigin () const;
virtual void setOrigin (const char *origin); virtual void setOrigin (const char *origin);
virtual const char* what() const throw(); virtual const char* what() const noexcept;
private: private:
char _message[MAX_TEXT_LEN]; char _message[MAX_TEXT_LEN];
char _origin[MAX_TEXT_LEN]; char _origin[MAX_TEXT_LEN];
@ -87,7 +87,7 @@ public:
sg_error (); sg_error ();
sg_error (const char* message, const char* origin = 0); sg_error (const char* message, const char* origin = 0);
sg_error (const std::string& message, const std::string& origin = ""); 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 ();
sg_exception (const char* message, const char* origin = 0); sg_exception (const char* message, const char* origin = 0);
sg_exception (const std::string& message, const std::string& = ""); sg_exception (const std::string& message, const std::string& = "");
virtual ~sg_exception () throw (); virtual ~sg_exception ();
}; };
@ -137,7 +137,7 @@ public:
sg_io_exception (const std::string &message, const sg_location &location, sg_io_exception (const std::string &message, const sg_location &location,
const std::string &origin = ""); const std::string &origin = "");
virtual ~sg_io_exception () throw (); virtual ~sg_io_exception ();
virtual const std::string getFormattedMessage () const; virtual const std::string getFormattedMessage () const;
virtual const sg_location &getLocation () const; virtual const sg_location &getLocation () const;
virtual void setLocation (const sg_location &location); virtual void setLocation (const sg_location &location);
@ -165,7 +165,7 @@ public:
const char* origin = 0); const char* origin = 0);
sg_format_exception (const std::string& message, const std::string& text, sg_format_exception (const std::string& message, const std::string& text,
const std::string& origin = ""); const std::string& origin = "");
virtual ~sg_format_exception () throw (); virtual ~sg_format_exception ();
virtual const char* getText () const; virtual const char* getText () const;
virtual void setText (const char* text); virtual void setText (const char* text);
private: private:
@ -190,7 +190,7 @@ public:
const char* origin = 0); const char* origin = 0);
sg_range_exception (const std::string& message, sg_range_exception (const std::string& message,
const std::string& origin = ""); const std::string& origin = "");
virtual ~sg_range_exception () throw (); virtual ~sg_range_exception ();
}; };
#endif #endif

View File

@ -1,6 +1,13 @@
/// Unit tests for reference counting and smart pointer classes // -*- coding: utf-8 -*-
#define BOOST_TEST_MODULE structure //
#include <BoostTestTargetConfig.h> // Unit tests for reference counting and smart pointer classes
#include <iostream>
#include <utility>
#include <cstdlib> // EXIT_SUCCESS
#include <simgear/misc/test_macros.hxx>
#include "SGSharedPtr.hxx" #include "SGSharedPtr.hxx"
#include "SGWeakPtr.hxx" #include "SGWeakPtr.hxx"
@ -21,33 +28,152 @@ struct ReferenceCounted:
}; };
typedef SGSharedPtr<ReferenceCounted> RefPtr; typedef SGSharedPtr<ReferenceCounted> 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() ); RefPtr ptr( new ReferenceCounted() );
BOOST_REQUIRE_EQUAL( instance_count, 1 ); SG_CHECK_EQUAL( instance_count, 1 );
BOOST_REQUIRE_EQUAL( ReferenceCounted::count(ptr.get()), 1 ); SG_CHECK_EQUAL( ReferenceCounted::count(ptr.get()), 1 );
BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 1 ); SG_CHECK_EQUAL( ptr.getNumRefs(), 1 );
// Test SGSharedPtr's copy assignment operator
RefPtr ptr2 = ptr; RefPtr ptr2 = ptr;
BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 2 ); SG_CHECK_EQUAL( ptr.getNumRefs(), 2 );
BOOST_REQUIRE_EQUAL( ptr2.getNumRefs(), 2 ); SG_CHECK_EQUAL( ptr2.getNumRefs(), 2 );
BOOST_REQUIRE_EQUAL( ptr, ptr2 ); SG_CHECK_EQUAL( ptr, ptr2 );
BOOST_REQUIRE_EQUAL( ptr.get(), ptr2.get() ); SG_CHECK_EQUAL( ptr.get(), ptr2.get() );
// Test SGSharedPtr::reset() with no argument
ptr.reset(); ptr.reset();
BOOST_REQUIRE( !ptr.get() ); SG_CHECK_IS_NULL( ptr.get() );
BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 0 ); SG_CHECK_EQUAL( ptr.getNumRefs(), 0 );
BOOST_REQUIRE_EQUAL( ReferenceCounted::count(ptr2.get()), 1 ); SG_CHECK_EQUAL( ReferenceCounted::count(ptr2.get()), 1 );
BOOST_REQUIRE_EQUAL( ptr2.getNumRefs(), 1 ); SG_CHECK_EQUAL( ptr2.getNumRefs(), 1 );
ptr2.reset(); ptr2.reset();
BOOST_REQUIRE( !ptr2.get() ); SG_CHECK_IS_NULL( ptr2.get() );
BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 0 ); SG_CHECK_EQUAL( ptr.getNumRefs(), 0 );
BOOST_REQUIRE_EQUAL( ptr2.getNumRefs(), 0 ); SG_CHECK_EQUAL( ptr2.getNumRefs(), 0 );
BOOST_REQUIRE_EQUAL( instance_count, 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: class Base1:
@ -63,31 +189,41 @@ class VirtualDerived:
public Base2 public Base2
{}; {};
BOOST_AUTO_TEST_CASE( virtual_weak_ptr ) void test_SGWeakPtr()
{ {
std::cout << "Testing SGWeakPtr and SGVirtualWeakReferenced" << std::endl;
SGSharedPtr<VirtualDerived> ptr( new VirtualDerived() ); SGSharedPtr<VirtualDerived> ptr( new VirtualDerived() );
SGWeakPtr<VirtualDerived> weak_ptr( ptr ); SGWeakPtr<VirtualDerived> weak_ptr( ptr );
BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 1 ); SG_CHECK_EQUAL( ptr.getNumRefs(), 1 );
SGSharedPtr<Base1> ptr1( weak_ptr.lock() ); SGSharedPtr<Base1> ptr1( weak_ptr.lock() );
BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 2 ); SG_CHECK_EQUAL( ptr.getNumRefs(), 2 );
// converting constructor // converting constructor
BOOST_REQUIRE_EQUAL( SGSharedPtr<Base1>(weak_ptr), ptr1 ); SG_CHECK_EQUAL( SGSharedPtr<Base1>(weak_ptr), ptr1 );
SGSharedPtr<Base2> ptr2( weak_ptr.lock() ); SGSharedPtr<Base2> ptr2( weak_ptr.lock() );
BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 3 ); SG_CHECK_EQUAL( ptr.getNumRefs(), 3 );
BOOST_REQUIRE( ptr != NULL ); SG_CHECK_IS_NOT_NULL( ptr );
BOOST_REQUIRE_EQUAL( ptr.get(), ptr1.get() ); SG_CHECK_EQUAL( ptr.get(), ptr1.get() );
BOOST_REQUIRE_EQUAL( ptr.get(), ptr2.get() ); SG_CHECK_EQUAL( ptr.get(), ptr2.get() );
SGWeakPtr<Base1> weak_base1( ptr ); SGWeakPtr<Base1> weak_base1( ptr );
SGWeakPtr<Base2> weak_base2( ptr ); SGWeakPtr<Base2> weak_base2( ptr );
ptr1 = dynamic_cast<VirtualDerived*>(weak_base1.lock().get()); ptr1 = dynamic_cast<VirtualDerived*>(weak_base1.lock().get());
ptr2 = dynamic_cast<VirtualDerived*>(weak_base2.lock().get()); ptr2 = dynamic_cast<VirtualDerived*>(weak_base2.lock().get());
BOOST_REQUIRE_EQUAL( ptr.get(), ptr1.get() ); SG_CHECK_EQUAL( ptr.get(), ptr1.get() );
BOOST_REQUIRE_EQUAL( ptr.get(), ptr2.get() ); SG_CHECK_EQUAL( ptr.get(), ptr2.get() );
BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 3 ); SG_CHECK_EQUAL( ptr.getNumRefs(), 3 );
}
int main(int argc, char* argv[])
{
test_SGSharedPtr();
test_SGWeakPtr();
return EXIT_SUCCESS;
} }

View File

@ -22,6 +22,8 @@
# include <simgear_config.h> # include <simgear_config.h>
#endif #endif
#include <algorithm>
#include <simgear/debug/logstream.hxx> #include <simgear/debug/logstream.hxx>
#include <simgear/timing/timestamp.hxx> #include <simgear/timing/timestamp.hxx>