diff --git a/test/unique_ref_test.cpp b/test/unique_ref_test.cpp index 55a0c7ab..5ef5b0ac 100644 --- a/test/unique_ref_test.cpp +++ b/test/unique_ref_test.cpp @@ -1,5 +1,10 @@ #include #include "../unique_ref.h" +#include +#include +#include +#include +#include using namespace cpputils; @@ -17,6 +22,7 @@ public: int param1; int param2; }; +using SomeClass = SomeClass0Parameters; TEST(MakeUniqueRefTest, Primitive) { unique_ref var = make_unique_ref(3); @@ -89,7 +95,7 @@ TEST(NullcheckTest, OptionIsResolvable_Primitive) { } TEST(NullcheckTest, OptionIsResolvable_Object) { - boost::optional> var = nullcheck(std::make_unique()); + boost::optional> var = nullcheck(std::make_unique()); unique_ref resolved = std::move(*var); } @@ -99,6 +105,300 @@ TEST(NullcheckTest, OptionIsAutoResolvable_Primitive) { } TEST(NullcheckTest, OptionIsAutoResolvable_Object) { - auto var = nullcheck(std::make_unique()); + auto var = nullcheck(std::make_unique()); auto resolved = std::move(*var); } + +class UniqueRefTest: public ::testing::Test { +public: + template void makeInvalid(unique_ref ref) { + UNUSED(ref); + //ref is moved in here and then destructed + } +}; + +TEST_F(UniqueRefTest, Get_Primitive) { + unique_ref obj = make_unique_ref(3); + EXPECT_EQ(3, *obj.get()); +} + +TEST_F(UniqueRefTest, Get_Object) { + unique_ref obj = make_unique_ref(5); + EXPECT_EQ(5, obj.get()->param); +} + +TEST_F(UniqueRefTest, Deref_Primitive) { + unique_ref obj = make_unique_ref(3); + EXPECT_EQ(3, *obj); +} + +TEST_F(UniqueRefTest, Deref_Object) { + unique_ref obj = make_unique_ref(5); + EXPECT_EQ(5, (*obj).param); +} + +TEST_F(UniqueRefTest, DerefArrow) { + unique_ref obj = make_unique_ref(3); + EXPECT_EQ(3, obj->param); +} + +TEST_F(UniqueRefTest, Assignment) { + unique_ref obj1 = make_unique_ref(); + unique_ref obj2 = make_unique_ref(); + SomeClass *obj1ptr = obj1.get(); + obj2 = std::move(obj1); + EXPECT_EQ(obj1ptr, obj2.get()); + EXPECT_EQ(nullptr, obj1.get()); +} + +TEST_F(UniqueRefTest, MoveConstructor) { + unique_ref obj1 = make_unique_ref(); + SomeClass *obj1ptr = obj1.get(); + unique_ref obj2 = std::move(obj1); + EXPECT_EQ(obj1ptr, obj2.get()); + EXPECT_EQ(nullptr, obj1.get()); +} + +TEST_F(UniqueRefTest, Swap) { + unique_ref obj1 = make_unique_ref(); + unique_ref obj2 = make_unique_ref(); + SomeClass *obj1ptr = obj1.get(); + SomeClass *obj2ptr = obj2.get(); + std::swap(obj1, obj2); + EXPECT_EQ(obj2ptr, obj1.get()); + EXPECT_EQ(obj1ptr, obj2.get()); +} + +TEST_F(UniqueRefTest, SwapFromInvalid) { + unique_ref obj1 = make_unique_ref(); + makeInvalid(std::move(obj1)); + unique_ref obj2 = make_unique_ref(); + SomeClass *obj2ptr = obj2.get(); + std::swap(obj1, obj2); + EXPECT_EQ(obj2ptr, obj1.get()); + EXPECT_EQ(nullptr, obj2.get()); +} + +TEST_F(UniqueRefTest, SwapWithInvalid) { + unique_ref obj1 = make_unique_ref(); + unique_ref obj2 = make_unique_ref(); + makeInvalid(std::move(obj2)); + SomeClass *obj1ptr = obj1.get(); + std::swap(obj1, obj2); + EXPECT_EQ(nullptr, obj1.get()); + EXPECT_EQ(obj1ptr, obj2.get()); +} + +TEST_F(UniqueRefTest, SwapInvalidWithInvalid) { + unique_ref obj1 = make_unique_ref(); + unique_ref obj2 = make_unique_ref(); + makeInvalid(std::move(obj1)); + makeInvalid(std::move(obj2)); + std::swap(obj1, obj2); + EXPECT_EQ(nullptr, obj1.get()); + EXPECT_EQ(nullptr, obj2.get()); +} + +TEST_F(UniqueRefTest, SwapFromRValue) { + unique_ref obj1 = make_unique_ref(); + unique_ref obj2 = make_unique_ref(); + SomeClass *obj1ptr = obj1.get(); + SomeClass *obj2ptr = obj2.get(); + std::swap(std::move(obj1), obj2); + EXPECT_EQ(obj2ptr, obj1.get()); + EXPECT_EQ(obj1ptr, obj2.get()); +} + +TEST_F(UniqueRefTest, SwapWithRValue) { + unique_ref obj1 = make_unique_ref(); + unique_ref obj2 = make_unique_ref(); + SomeClass *obj1ptr = obj1.get(); + SomeClass *obj2ptr = obj2.get(); + std::swap(obj1, std::move(obj2)); + EXPECT_EQ(obj2ptr, obj1.get()); + EXPECT_EQ(obj1ptr, obj2.get()); +} + +TEST_F(UniqueRefTest, CanBePutInContainer_Primitive) { + std::vector> vec; + vec.push_back(make_unique_ref(3)); + EXPECT_EQ(3, *vec[0]); +} + +TEST_F(UniqueRefTest, CanBePutInContainer_Object) { + std::vector> vec; + vec.push_back(make_unique_ref(5)); + EXPECT_EQ(5, vec[0]->param); +} + +TEST_F(UniqueRefTest, CanBePutInContainer_Nullcheck) { + std::vector> vec; + vec.push_back(*nullcheck(std::make_unique(3))); + EXPECT_EQ(3, *vec[0]); +} + +TEST_F(UniqueRefTest, CanBePutInSet_Primitive) { + std::set> set; + set.insert(make_unique_ref(3)); + EXPECT_EQ(3, **set.begin()); +} + +TEST_F(UniqueRefTest, CanBePutInSet_Object) { + std::set> set; + set.insert(make_unique_ref(5)); + EXPECT_EQ(5, (*set.begin())->param); +} + +TEST_F(UniqueRefTest, CanBePutInSet_Nullcheck) { + std::set> set; + set.insert(*nullcheck(std::make_unique(3))); + EXPECT_EQ(3, **set.begin()); +} + +TEST_F(UniqueRefTest, CanBePutInUnorderedSet_Primitive) { + std::unordered_set> set; + set.insert(make_unique_ref(3)); + EXPECT_EQ(3, **set.begin()); +} + +TEST_F(UniqueRefTest, CanBePutInUnorderedSet_Object) { + std::unordered_set> set; + set.insert(make_unique_ref(5)); + EXPECT_EQ(5, (*set.begin())->param); +} + +TEST_F(UniqueRefTest, CanBePutInUnorderedSet_Nullcheck) { + std::unordered_set> set; + set.insert(*nullcheck(std::make_unique(3))); + EXPECT_EQ(3, **set.begin()); +} + +TEST_F(UniqueRefTest, CanBePutInMap_Primitive) { + std::map, unique_ref> map; + map.insert(std::make_pair(make_unique_ref(3), make_unique_ref(5))); + EXPECT_EQ(3, *map.begin()->first); + EXPECT_EQ(5, *map.begin()->second); +} + +TEST_F(UniqueRefTest, CanBePutInMap_Object) { + std::map, unique_ref> map; + map.insert(std::make_pair(make_unique_ref(5), make_unique_ref(3))); + EXPECT_EQ(5, map.begin()->first->param); + EXPECT_EQ(3, map.begin()->second->param); +} + +TEST_F(UniqueRefTest, CanBePutInMap_Nullcheck) { + std::map, unique_ref> map; + map.insert(std::make_pair(*nullcheck(std::make_unique(3)), *nullcheck(std::make_unique(5)))); + EXPECT_EQ(3, *map.begin()->first); + EXPECT_EQ(5, *map.begin()->second); +} + +TEST_F(UniqueRefTest, CanBePutInUnorderedMap_Primitive) { + std::unordered_map, unique_ref> map; + map.insert(std::make_pair(make_unique_ref(3), make_unique_ref(5))); + EXPECT_EQ(3, *map.begin()->first); + EXPECT_EQ(5, *map.begin()->second); +} + +TEST_F(UniqueRefTest, CanBePutInUnorderedMap_Object) { + std::unordered_map, unique_ref> map; + map.insert(std::make_pair(make_unique_ref(5), make_unique_ref(3))); + EXPECT_EQ(5, map.begin()->first->param); + EXPECT_EQ(3, map.begin()->second->param); +} + +TEST_F(UniqueRefTest, CanBePutInUnorderedMap_Nullcheck) { + std::unordered_map, unique_ref> map; + map.insert(std::make_pair(*nullcheck(std::make_unique(3)), *nullcheck(std::make_unique(5)))); + EXPECT_EQ(3, *map.begin()->first); + EXPECT_EQ(5, *map.begin()->second); +} + +TEST_F(UniqueRefTest, Equality_Nullptr) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(4); + makeInvalid(std::move(var1)); + makeInvalid(std::move(var2)); + EXPECT_TRUE(var1 == var2); + EXPECT_FALSE(var1 != var2); +} + +TEST_F(UniqueRefTest, Nonequality) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(3); + EXPECT_TRUE(var1 != var2); + EXPECT_FALSE(var1 == var2); +} + +TEST_F(UniqueRefTest, Nonequality_NullptrLeft) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(3); + makeInvalid(std::move(var1)); + EXPECT_TRUE(var1 != var2); + EXPECT_FALSE(var1 == var2); +} + +TEST_F(UniqueRefTest, Nonequality_NullptrRight) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(3); + makeInvalid(std::move(var2)); + EXPECT_TRUE(var1 != var2); + EXPECT_FALSE(var1 == var2); +} + +TEST_F(UniqueRefTest, HashIsDifferent) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(3); + EXPECT_NE(std::hash>()(var1), std::hash>()(var2)); +} + +TEST_F(UniqueRefTest, HashIsDifferent_NullptrLeft) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(3); + makeInvalid(std::move(var1)); + EXPECT_NE(std::hash>()(var1), std::hash>()(var2)); +} + +TEST_F(UniqueRefTest, HashIsDifferent_NullptrRight) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(3); + makeInvalid(std::move(var2)); + EXPECT_NE(std::hash>()(var1), std::hash>()(var2)); +} + +TEST_F(UniqueRefTest, HashIsSame_BothNullptr) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(3); + makeInvalid(std::move(var1)); + makeInvalid(std::move(var2)); + EXPECT_EQ(std::hash>()(var1), std::hash>()(var2)); +} + +TEST_F(UniqueRefTest, OneIsLess) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(3); + EXPECT_TRUE(std::less>()(var1, var2) != std::less>()(var2, var1)); +} + +TEST_F(UniqueRefTest, NullptrIsLess1) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(3); + makeInvalid(std::move(var1)); + EXPECT_TRUE(std::less>()(var1, var2)); +} + +TEST_F(UniqueRefTest, NullptrIsLess2) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(3); + makeInvalid(std::move(var2)); + EXPECT_TRUE(std::less>()(var2, var1)); +} + +TEST_F(UniqueRefTest, NullptrIsNotLessThanNullptr) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(3); + makeInvalid(std::move(var1)); + makeInvalid(std::move(var2)); + EXPECT_FALSE(std::less>()(var1, var2)); +} diff --git a/unique_ref.h b/unique_ref.h index c426b3b5..fe1e4915 100644 --- a/unique_ref.h +++ b/unique_ref.h @@ -28,7 +28,8 @@ public: unique_ref(unique_ref&& from): _target(std::move(from._target)) {} unique_ref& operator=(unique_ref&& from) { - _target = from._target; + _target = std::move(from._target); + return *this; } typename std::add_lvalue_reference::type operator*() const { @@ -43,8 +44,8 @@ public: return _target.get(); } - void swap(unique_ref&& rhs) { - _target.swap(rhs._target); + void swap(unique_ref& rhs) { + std::swap(_target, rhs._target); } private: @@ -70,34 +71,14 @@ inline boost::optional> nullcheck(std::unique_ptr ptr) { return boost::none; } -template -inline bool operator==(const unique_ref& lhs, const unique_ref& rhs) { - return lhs.get() == rhs.get(); +template +inline bool operator==(const unique_ref &lhs, const unique_ref &rhs) { + return lhs.get() == rhs.get(); } -template -inline bool operator!=(const unique_ref& lhs, const unique_ref& rhs) { - return !operator==(lhs, rhs); -} - -template -inline bool operator<(const unique_ref& lhs, const unique_ref& rhs) { - return lhs.get() < rhs.get(); -} - -template -inline bool operator<=(const unique_ref& lhs, const unique_ref& rhs) { - return !operator<(rhs, lhs); -} - -template -inline bool operator>(const unique_ref& lhs, const unique_ref& rhs) { - return operator<(rhs, lhs); -} - -template -inline bool operator>=(const unique_ref& lhs, const unique_ref& rhs) { - return !operator<(lhs, rhs); +template +inline bool operator!=(const unique_ref &lhs, const unique_ref &rhs) { + return !operator==(lhs, rhs); } } @@ -117,6 +98,20 @@ namespace std { inline void swap(cpputils::unique_ref& lhs, cpputils::unique_ref&& rhs) { lhs.swap(rhs); } + + // Allow using it in std::unordered_set / std::unordered_map + template struct hash> { + size_t operator()(const cpputils::unique_ref &ref) const { + return (size_t)ref.get(); + } + }; + + // Allow using it in std::map / std::set + template struct less> { + bool operator()(const cpputils::unique_ref &lhs, const cpputils::unique_ref &rhs) const { + return lhs.get() < rhs.get(); + } + }; } #endif