diff --git a/Foundation/include/Poco/Any.h b/Foundation/include/Poco/Any.h index 31c13f005..9243dcb97 100644 --- a/Foundation/include/Poco/Any.h +++ b/Foundation/include/Poco/Any.h @@ -39,6 +39,7 @@ #include "Poco/Exception.h" +#include "Poco/MetaProgramming.h" #include #include @@ -84,6 +85,8 @@ public: } ~Any() + /// Destructor. If Any is locally held, calls Placeholder destructor; + /// otherwise, deletes the placeholder from the heap. { if(!empty()) { @@ -94,24 +97,44 @@ public: } } + Any& swap(Any& other) + /// Swaps the content of the two Anys. + /// + /// When small object optimizaton (SOO) is enabled, + /// swap is only exception-safe when both (*this and + /// other) objects are allocated on the heap. + { + if (!isLocal() && !other.isLocal()) + { + std::swap(_placeholder.pHolder, other._placeholder.pHolder); + } + else + { + Any tmp(*this); + if (isLocal()) this->~Any(); + construct(other); + other = tmp; + } + + return *this; + } + template - Any & operator=(const ValueType& value) + Any & operator = (const ValueType& rhs) /// Assignment operator for all types != Any. /// /// Example: /// Any a = 13; /// Any a = string("12345"); { - if (isLocal()) this->~Any(); - construct(value); + Any(rhs).swap(*this); return *this; } - Any& operator = (Any value) + Any& operator = (Any rhs) /// Assignment operator for Any. { - if (isLocal()) this->~Any(); - construct(value); + Any(rhs).swap(*this); return *this; } @@ -192,7 +215,7 @@ private: } template - void construct(const ValueType & value) + void construct(const ValueType& value) { if (sizeof(Holder) <= POCO_SMALL_OBJECT_SIZE) { @@ -206,7 +229,7 @@ private: } } - void construct(const Any & other) + void construct(const Any& other) { if(other.empty()) erase(_placeholder.holder); @@ -408,9 +431,9 @@ ValueType AnyCast(const Any& operand) /// Some compilers will accept this code although a copy is returned. Use the RefAnyCast in /// these cases. { - ValueType* result = AnyCast(const_cast(&operand)); - if (!result) throw BadCastException("Failed to convert between const Any types"); - return *result; + typedef TypeWrapper::TYPE NonRef; + + return AnyCast(const_cast(operand)); } @@ -425,7 +448,9 @@ ValueType AnyCast(Any& operand) /// Some compilers will accept this code although a copy is returned. Use the RefAnyCast in /// these cases. { - ValueType* result = AnyCast(&operand); + typedef TypeWrapper::TYPE NonRef; + + NonRef* result = AnyCast(&operand); if (!result) throw BadCastException("Failed to convert between Any types"); return *result; } diff --git a/Foundation/testsuite/src/AnyTest.cpp b/Foundation/testsuite/src/AnyTest.cpp index 3fee242d9..0c456e9d3 100644 --- a/Foundation/testsuite/src/AnyTest.cpp +++ b/Foundation/testsuite/src/AnyTest.cpp @@ -70,6 +70,156 @@ AnyTest::~AnyTest() } +void AnyTest::testDefaultCtor() +{ + const Any value; + + assert (value.empty()); + assert (0 == AnyCast(&value)); + assert (value.type() == typeid(void)); +} + + +void AnyTest::testConvertingCtor() +{ + std::string text = "test message"; + Any value = text; + + assert (!value.empty()); + assert (value.type() == typeid(std::string)); + assert (0 == AnyCast(&value)); + assert (0 != AnyCast(&value)); + assert (AnyCast(value) == text); + assert (AnyCast(&value) != &text); +} + + +void AnyTest::testCopyCtor() +{ + std::string text = "test message"; + Any original = text, copy = original; + + assert (!copy.empty()); + assert (original.type() == copy.type()); + assert (AnyCast(original) == AnyCast(copy)); + assert (text == AnyCast(copy)); + assert(AnyCast(&original) != AnyCast(©)); +} + + +void AnyTest::testCopyAssign() +{ + std::string text = "test message"; + Any original = text, copy; + Any* assignResult = &(copy = original); + + assert(!copy.empty()); + assert(original.type() == copy.type()); + assert(AnyCast(original) == AnyCast(copy)); + assert(text == AnyCast(copy)); + assert(AnyCast(&original) != AnyCast(©)); + assert(assignResult == ©); +} + + +void AnyTest::testConvertingAssign() +{ + std::string text = "test message"; + Any value; + Any* assignResult = &(value = text); + + assert (!value.empty()); + assert(value.type() == typeid(std::string)); + assert(0 == AnyCast(&value)); + assert(0 != AnyCast(&value)); + assert(AnyCast(value) == text); + assert(AnyCast(&value) != &text); + assert(assignResult == &value); +} + + +void AnyTest::testCastToReference() +{ + Any a(137); + const Any b(a); + + int& ra = AnyCast(a); + int const& ra_c = AnyCast(a); + int volatile& ra_v = AnyCast(a); + int const volatile& ra_cv = AnyCast(a); + + // cv references to same obj + assert (&ra == &ra_c && &ra == &ra_v && &ra == &ra_cv); + + int const & rb_c = AnyCast(b); + int const volatile & rb_cv = AnyCast(b); + + assert (&rb_c == &rb_cv); // cv references to copied const obj + assert (&ra != &rb_c); // copies hold different objects + + ++ra; + int incremented = AnyCast(a); + assert (incremented == 138); // increment by reference changes value + + try + { + AnyCast(a); + failmsg ("AnyCast to incorrect reference type"); + } + catch (BadCastException&) { } + + try + { + AnyCast(b), + failmsg ("AnyCast to incorrect const reference type"); + } + catch (BadCastException&) { } +} + + +void AnyTest::testBadCast() +{ + std::string text = "test message"; + Any value = text; + + try + { + AnyCast(value); + fail ("must throw"); + } + catch (BadCastException&) { } +} + + +void AnyTest::testSwap() +{ + std::string text = "test message"; + Any original = text, swapped; + std::string* originalPtr = AnyCast(&original); + Any* swapResult = &original.swap(swapped); + + assert (original.empty()); + assert (!swapped.empty()); + assert (swapped.type() == typeid(std::string)); + assert (text == AnyCast(swapped)); + assert (0 != originalPtr); + assert (originalPtr == AnyCast(&swapped)); + assert (swapResult == &original); +} + + +void AnyTest::testEmptyCopy() +{ + const Any null; + Any copied = null, assigned; + assigned = null; + + assert (null.empty()); + assert (copied.empty()); + assert (assigned.empty()); +} + + void AnyTest::testInt() { Any e; @@ -119,8 +269,13 @@ void AnyTest::testVector() Any a = tmp; assert (a.type() == typeid(std::vector)); std::vector tmp2 = AnyCast >(a); + assert (tmp2.size() == 3); const std::vector& vecCRef = RefAnyCast >(a); std::vector& vecRef = RefAnyCast >(a); + + assert (vecRef[0] == 1); + assert (vecRef[1] == 2); + assert (vecRef[2] == 3); vecRef[0] = 0; assert (vecRef[0] == vecCRef[0]); } @@ -140,6 +295,15 @@ CppUnit::Test* AnyTest::suite() { CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("AnyTest"); + CppUnit_addTest(pSuite, AnyTest, testConvertingCtor); + CppUnit_addTest(pSuite, AnyTest, testDefaultCtor); + CppUnit_addTest(pSuite, AnyTest, testCopyCtor); + CppUnit_addTest(pSuite, AnyTest, testCopyAssign); + CppUnit_addTest(pSuite, AnyTest, testConvertingAssign); + CppUnit_addTest(pSuite, AnyTest, testBadCast); + CppUnit_addTest(pSuite, AnyTest, testSwap); + CppUnit_addTest(pSuite, AnyTest, testEmptyCopy); + CppUnit_addTest(pSuite, AnyTest, testCastToReference); CppUnit_addTest(pSuite, AnyTest, testInt); CppUnit_addTest(pSuite, AnyTest, testComplexType); CppUnit_addTest(pSuite, AnyTest, testVector); diff --git a/Foundation/testsuite/src/AnyTest.h b/Foundation/testsuite/src/AnyTest.h index 0a1302836..246cb8124 100644 --- a/Foundation/testsuite/src/AnyTest.h +++ b/Foundation/testsuite/src/AnyTest.h @@ -45,6 +45,16 @@ public: AnyTest(const std::string& name); ~AnyTest(); + void testConvertingCtor(); + void testDefaultCtor(); + void testCopyCtor(); + void testCopyAssign(); + void testConvertingAssign(); + void testBadCast(); + void testSwap(); + void testEmptyCopy(); + void testCastToReference(); + void testInt(); void testComplexType(); void testVector();