From fcf7e786b07fc2daa6a934abef3813319a9ebee8 Mon Sep 17 00:00:00 2001 From: Alex Fabijanic Date: Mon, 27 Jun 2022 17:03:08 +0200 Subject: [PATCH] fix(Any): As of C++11, std::swap is noexcept. #2386 --- Foundation/include/Poco/Any.h | 29 +++++++++++++-- Foundation/include/Poco/Config.h | 2 +- Foundation/testsuite/src/AnyTest.cpp | 53 ++++++++++++++++++++++++++-- 3 files changed, 78 insertions(+), 6 deletions(-) diff --git a/Foundation/include/Poco/Any.h b/Foundation/include/Poco/Any.h index 98eb5dbb67..ef325270b3 100644 --- a/Foundation/include/Poco/Any.h +++ b/Foundation/include/Poco/Any.h @@ -23,6 +23,9 @@ #include +#define poco_any_assert(cond) do { if (!(cond)) std::abort(); } while (0) + + namespace Poco { class Any; @@ -56,6 +59,9 @@ union Placeholder /// (i.e. there will be no heap-allocation). The local buffer size is one byte /// larger - [POCO_SMALL_OBJECT_SIZE + 1], additional byte value indicating /// where the object was allocated (0 => heap, 1 => local). + /// + /// Important: for SOO builds, only same-type (or trivial both-empty no-op) + /// swap operation is allowed. { public: struct Size @@ -82,8 +88,22 @@ union Placeholder void swap(Placeholder& other) noexcept { - poco_assert (isLocal() && other.isLocal()); - std::swap(pHolder, other.pHolder); + bool empty = isEmpty(); + bool otherEmpty = other.isEmpty(); + bool local = isLocal(); + bool otherLocal = other.isLocal(); + + if (empty && otherEmpty) return; + poco_any_assert (local == otherLocal); + if (!local) std::swap(pHolder, other.pHolder); + else + { + unsigned int sz = SizeV + 1; + unsigned char tmpHolder[sz] = {}; + std::memcpy(tmpHolder, holder, sz); + std::memcpy(holder, other.holder, sz); + std::memcpy(other.holder, tmpHolder, sz); + } } void erase() @@ -314,6 +334,11 @@ class Any return empty() ? typeid(void) : content()->type(); } + bool local() const + { + return _valueHolder.isLocal(); + } + private: class ValueHolder { diff --git a/Foundation/include/Poco/Config.h b/Foundation/include/Poco/Config.h index 9cfa370808..33845ce823 100644 --- a/Foundation/include/Poco/Config.h +++ b/Foundation/include/Poco/Config.h @@ -79,7 +79,7 @@ // while those smaller will be placement new-ed into an // internal stack-auto-allocated buffer. #if !defined(POCO_SMALL_OBJECT_SIZE) - #define POCO_SMALL_OBJECT_SIZE 32 + #define POCO_SMALL_OBJECT_SIZE 64 #endif diff --git a/Foundation/testsuite/src/AnyTest.cpp b/Foundation/testsuite/src/AnyTest.cpp index b8ed755143..0362bb4d40 100644 --- a/Foundation/testsuite/src/AnyTest.cpp +++ b/Foundation/testsuite/src/AnyTest.cpp @@ -186,6 +186,14 @@ void AnyTest::testAnySwap() { std::string text = "test message"; Any original = text, swapped; +#ifdef POCO_NO_SOO + assertFalse (original.local()); +#else + assertTrue (original.local()); +#endif + assertFalse (original.empty()); + assertFalse (swapped.local()); + assertTrue (swapped.empty()); std::string* originalPtr = AnyCast(&original); Any* swapResult = &original.swap(swapped); @@ -194,10 +202,49 @@ void AnyTest::testAnySwap() assertTrue (swapped.type() == typeid(std::string)); assertTrue (text == AnyCast(swapped)); assertTrue (0 != originalPtr); -#ifdef POCO_NO_SOO // pointers only match when heap-allocated - assertTrue (originalPtr == AnyCast(&swapped)); -#endif assertTrue (swapResult == &original); + + struct BigObject + { + Poco::UInt64 one = 1; + Poco::UInt64 two = 2; + Poco::UInt64 three = 3; + Poco::UInt64 four = 4; + Poco::UInt64 five = 5; + Poco::UInt64 six = 6; + Poco::UInt64 seven = 7; + Poco::UInt64 eight = 8; + Poco::UInt64 nine = 9; + + bool operator==(const BigObject& other) + { + return one == other.one && + two == other.two && + three == other.three && + four == other.four && + five == other.five && + six == other.six && + seven == other.seven && + eight == other.eight && + nine == other.nine; + } + }; + + BigObject bigObject; + Any bigOriginal = bigObject, swappedBig; + assertFalse (bigOriginal.local()); + assertFalse (bigOriginal.empty()); + assertFalse (swappedBig.local()); + assertTrue (swappedBig.empty()); + BigObject* bigPtr = AnyCast(&bigOriginal); + Any* swapBigResult = &bigOriginal.swap(swappedBig); + + assertTrue (bigOriginal.empty()); + assertTrue (!swappedBig.empty()); + assertTrue (swappedBig.type() == typeid(BigObject)); + assertTrue (bigObject == AnyCast(swappedBig)); + assertTrue (0 != bigPtr); + assertTrue (swapBigResult == &bigOriginal); }