From 60765fcaafabfc3b26fca2a2ab5fc0c609a55052 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sat, 4 May 2019 17:44:47 +0700 Subject: [PATCH] first flat_map impl and tests --- flat_map.hpp | 127 +++++++++++++------------ flat_map_tests.cpp | 225 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 245 insertions(+), 107 deletions(-) diff --git a/flat_map.hpp b/flat_map.hpp index c223ad5..b117b2a 100644 --- a/flat_map.hpp +++ b/flat_map.hpp @@ -22,7 +22,27 @@ namespace flat_hpp , typename Compare = std::less , typename Allocator = std::allocator> > class flat_map final { - using data_type = std::vector, Allocator>; + using data_type = std::vector< + std::pair, + Allocator>; + + class uber_comparer_type : public Compare { + public: + uber_comparer_type() = default; + uber_comparer_type(const Compare& c) : Compare(c) {} + + bool operator()(const Key& l, const Key& r) const { + return Compare::operator()(l, r); + } + + bool operator()(const Key& l, typename data_type::const_reference r) const { + return Compare::operator()(l, r.first); + } + + bool operator()(typename data_type::const_reference l, const Key& r) const { + return Compare::operator()(l.first, r); + } + }; public: using key_type = Key; using mapped_type = Value; @@ -44,7 +64,7 @@ namespace flat_hpp using reverse_iterator = typename data_type::reverse_iterator; using const_reverse_iterator = typename data_type::const_reverse_iterator; - class value_compare final : public std::binary_function { + class value_compare final { public: bool operator()(const value_type& l, const value_type& r) const { return compare_(l.first, r.first); @@ -92,19 +112,19 @@ namespace flat_hpp } flat_map( - std::initializer_list il, + std::initializer_list ilist, const Allocator& a) : data_(a) { - insert(il.begin(), il.end()); + insert(ilist); } flat_map( - std::initializer_list il, + std::initializer_list ilist, const Compare& c = Compare(), const Allocator& a = Allocator()) : data_(a) , compare_(c) { - insert(il.begin(), il.end()); + insert(ilist); } iterator begin() noexcept { return data_.begin(); } @@ -160,36 +180,37 @@ namespace flat_hpp } std::pair insert(const value_type& value) { - bool found = true; - iterator iter = lower_bound(value.first); - if ( iter == end() || compare_(value.first, iter->first) ) { - iter = data_.insert(iter, value); - found = false; - } - return std::make_pair(iter, !found); + const iterator iter = lower_bound(value.first); + return iter == end() || compare_(value.first, iter->first) + ? std::make_pair(data_.insert(iter, value), true) + : std::make_pair(iter, false); } - std::pair insert(const_iterator hint, const value_type& value) { - //TODO(BlackMat): implme - return insert(value); + iterator insert(const_iterator hint, const value_type& value) { + return (hint == begin() || compare_((hint - 1)->first, value.first)) + && (hint == end() || compare_(value.first, hint->first)) + ? data_.insert(hint, std::move(value)) + : insert(std::move(value)).first; } template < typename InputIter > void insert(InputIter first, InputIter last) { - for ( auto iter = first; iter != last; ++iter ) { - insert(*iter); + while ( first != last ) { + insert(*first++); } } + void insert(std::initializer_list ilist) { + insert(ilist.begin(), ilist.end()); + } + template < typename... Args > std::pair emplace(Args&&... args) { - //TODO(BlackMat): implme return insert(value_type(std::forward(args)...)); } template < typename... Args > - std::pair emplace_hint(const_iterator hint, Args&&... args) { - //TODO(BlackMat): implme + iterator emplace_hint(const_iterator hint, Args&&... args) { return insert(hint, value_type(std::forward(args)...)); } @@ -198,22 +219,24 @@ namespace flat_hpp } iterator erase(const_iterator iter) { - //TODO(BlackMat): implme - return end(); + return data_.erase(iter); } iterator erase(const_iterator first, const_iterator last) { - //TODO(BlackMat): implme - return end(); + return data_.erase(first, last); } - iterator erase(const key_type& key) { - //TODO(BlackMat): implme - return end(); + size_type erase(const key_type& key) { + const iterator iter = find(key); + return iter != end() + ? (erase(iter), 1) + : 0; } void swap(flat_map& other) { - //TODO(BlackMat): implme + using std::swap; + swap(data_, other.data_); + swap(compare_, other.compare_); } size_type count(const key_type& key) const { @@ -222,57 +245,41 @@ namespace flat_hpp } iterator find(const key_type& key) { - iterator iter = lower_bound(key); - if ( iter != end() && compare_(key, iter->first) ) { - iter = end(); - } - return iter; + const iterator iter = lower_bound(key); + return iter != end() && !compare_(key, iter->first) + ? iter + : end(); } const_iterator find(const key_type& key) const { - const_iterator iter = lower_bound(key); - if ( iter != end() && compare_(key, iter->first) ) { - iter = end(); - } - return iter; + const const_iterator iter = lower_bound(key); + return iter != end() && !compare_(key, iter->first) + ? iter + : end(); } std::pair equal_range(const key_type& key) { - //TODO(BlackMat): implme - return {end(), end()}; + return std::equal_range(begin(), end(), key, compare_); } std::pair equal_range(const key_type& key) const { - //TODO(BlackMat): implme - return {end(), end()}; + return std::equal_range(begin(), end(), key, compare_); } iterator lower_bound(const key_type& key) { - //TODO(BlackMat): implme - return std::lower_bound(begin(), end(), key, [this](const value_type& l, const key_type& r){ - return compare_(l.first, r); - }); + return std::lower_bound(begin(), end(), key, compare_); } const_iterator lower_bound(const key_type& key) const { - //TODO(BlackMat): implme - return std::lower_bound(begin(), end(), key, [this](const value_type& l, const key_type& r){ - return compare_(l.first, r); - }); + return std::lower_bound(begin(), end(), key, compare_); } iterator upper_bound(const key_type& key) { - //TODO(BlackMat): implme - return std::upper_bound(begin(), end(), key, [this](const key_type& l, const value_type& r){ - return compare_(l, r.first); - }); + return std::upper_bound(begin(), end(), key, compare_); } const_iterator upper_bound(const key_type& key) const { - //TODO(BlackMat): implme - return std::upper_bound(begin(), end(), key, [this](const key_type& l, const value_type& r){ - return compare_(l, r.first); - }); + return std::upper_bound(begin(), end(), key, compare_); } key_compare key_comp() const { @@ -284,7 +291,7 @@ namespace flat_hpp } private: data_type data_; - key_compare compare_; + uber_comparer_type compare_; }; } diff --git a/flat_map_tests.cpp b/flat_map_tests.cpp index aab7a16..fa2eeeb 100644 --- a/flat_map_tests.cpp +++ b/flat_map_tests.cpp @@ -78,19 +78,32 @@ TEST_CASE("flat_map") { std::less, alloc_t>; + using map2_t = flat_map< + int, + unsigned, + std::greater, + alloc_t>; + { auto s0 = map_t(); - auto s1 = map_t(alloc_t()); + auto s1 = map2_t(alloc_t()); auto s2 = map_t(std::less()); - auto s3 = map_t(std::less(), alloc_t()); + auto s3 = map2_t(std::greater(), alloc_t()); } { - std::vector> v; + using vec_t = std::vector>; + + vec_t v{{1,30},{2,20},{3,10}}; auto s0 = map_t(v.cbegin(), v.cend()); - auto s1 = map_t(v.cbegin(), v.cend(), alloc_t()); + auto s1 = map2_t(v.cbegin(), v.cend(), alloc_t()); auto s2 = map_t(v.cbegin(), v.cend(), std::less()); - auto s3 = map_t(v.cbegin(), v.cend(), std::less(), alloc_t()); + auto s3 = map2_t(v.cbegin(), v.cend(), std::greater(), alloc_t()); + + REQUIRE(vec_t(s0.begin(), s0.end()) == vec_t({{1,30},{2,20},{3,10}})); + REQUIRE(vec_t(s1.begin(), s1.end()) == vec_t({{3,10},{2,20},{1,30}})); + REQUIRE(vec_t(s2.begin(), s2.end()) == vec_t({{1,30},{2,20},{3,10}})); + REQUIRE(vec_t(s3.begin(), s3.end()) == vec_t({{3,10},{2,20},{1,30}})); } { @@ -103,16 +116,40 @@ TEST_CASE("flat_map") { SECTION("capacity") { using map_t = flat_map; map_t s0; - s0.empty(); - s0.size(); - s0.max_size(); + + REQUIRE(s0.empty()); + REQUIRE_FALSE(s0.size()); + REQUIRE(s0.max_size() == std::allocator>().max_size()); + + s0.insert({2,42}); + + REQUIRE_FALSE(s0.empty()); + REQUIRE(s0.size() == 1u); + REQUIRE(s0.max_size() == std::allocator>().max_size()); + + s0.insert({2,84}); + REQUIRE(s0.size() == 1u); + + s0.insert({3,84}); + REQUIRE(s0.size() == 2u); + + s0.clear(); + + REQUIRE(s0.empty()); + REQUIRE_FALSE(s0.size()); + REQUIRE(s0.max_size() == std::allocator>().max_size()); } SECTION("access") { using map_t = flat_map; map_t s0; s0[1] = 42; - s0.at(1); - my_as_const(s0).at(1); + REQUIRE(s0 == map_t{{1,42}}); + s0[1] = 84; + REQUIRE(s0 == map_t{{1,84}}); + + REQUIRE(s0.at(1) == 84); + REQUIRE(my_as_const(s0).at(1) == 84); + REQUIRE_THROWS_AS(s0.at(0), std::out_of_range); } SECTION("inserts") { struct obj_t { @@ -122,61 +159,155 @@ TEST_CASE("flat_map") { bool operator<(const obj_t& o) const { return i < o.i; } + + bool operator==(const obj_t& o) const { + return i == o.i; + } }; using map_t = flat_map; { map_t s0; - s0.insert(std::make_pair(1, 42)); - s0.insert(std::make_pair(2, obj_t(42))); - s0.insert(s0.cend(), std::make_pair(3, 84)); + + auto i0 = s0.insert(std::make_pair(1, 42)); + REQUIRE(s0 == map_t{{1,42}}); + REQUIRE(i0 == std::make_pair(s0.begin(), true)); + + auto i1 = s0.insert(std::make_pair(1, obj_t(42))); + REQUIRE(s0 == map_t{{1,42}}); + REQUIRE(i1 == std::make_pair(s0.begin(), false)); + + auto i2 = s0.insert(std::make_pair(2, obj_t(42))); + REQUIRE(s0 == map_t{{1,42},{2,42}}); + REQUIRE(i2 == std::make_pair(s0.begin() + 1, true)); + + auto i3 = s0.insert(s0.cend(), std::make_pair(3, 84)); + REQUIRE(i3 == s0.begin() + 2); + s0.insert(s0.cend(), std::make_pair(4, obj_t(84))); - s0.emplace(5, 100500); - s0.emplace_hint(s0.cend(), 6, 100500); + + auto i4 = s0.emplace(5, 100500); + REQUIRE(i4 == std::make_pair(s0.end() - 1, true)); + REQUIRE(s0 == map_t{{1,42},{2,42},{3,84},{4,84},{5,100500}}); + + auto i5 = s0.emplace_hint(s0.cend(), 6, 100500); + REQUIRE(s0 == map_t{{1,42},{2,42},{3,84},{4,84},{5,100500},{6,100500}}); } } SECTION("erasers") { using map_t = flat_map; - map_t s0; - s0.clear(); - s0.erase(s0.begin()); - s0.erase(s0.cbegin()); - s0.erase(s0.begin(), s0.end()); - s0.erase(s0.cbegin(), s0.cend()); - s0.erase(42); - map_t s1; - s0.swap(s1); - swap(s0, s1); + { + map_t s0{{1,2},{2,3},{3,4}}; + s0.clear(); + REQUIRE(s0.empty()); + } + { + map_t s0{{1,2},{2,3},{3,4}}; + auto i = s0.erase(s0.find(2)); + REQUIRE(i == s0.begin() + 1); + REQUIRE(s0 == map_t{{1,2},{3,4}}); + } + { + map_t s0{{1,2},{2,3},{3,4}}; + auto i = s0.erase(s0.begin() + 1, s0.end()); + REQUIRE(i == s0.end()); + REQUIRE(s0 == map_t{{1,2}}); + } + { + map_t s0{{1,2},{2,3},{3,4}}; + REQUIRE(s0.erase(1) == 1); + REQUIRE(s0.erase(6) == 0); + REQUIRE(s0 == map_t{{2,3},{3,4}}); + } + { + map_t s0{{1,2},{2,3},{3,4}}; + map_t s1{{2,3},{3,4},{5,6}}; + s0.swap(s1); + REQUIRE(s0 == map_t{{2,3},{3,4},{5,6}}); + REQUIRE(s1 == map_t{{1,2},{2,3},{3,4}}); + swap(s1, s0); + REQUIRE(s0 == map_t{{1,2},{2,3},{3,4}}); + REQUIRE(s1 == map_t{{2,3},{3,4},{5,6}}); + } } SECTION("lookup") { using map_t = flat_map; - map_t s0; - s0.count(10); - s0.find(10); - my_as_const(s0).find(10); - s0.equal_range(20); - my_as_const(s0).equal_range(20); - s0.lower_bound(30); - my_as_const(s0).lower_bound(30); - s0.upper_bound(30); - my_as_const(s0).upper_bound(30); + { + map_t s0{{1,2},{2,3},{3,4},{4,5},{5,6}}; + REQUIRE(s0.count(3)); + REQUIRE_FALSE(s0.count(6)); + REQUIRE(my_as_const(s0).count(5)); + REQUIRE_FALSE(my_as_const(s0).count(0)); + } + { + map_t s0{{1,2},{2,3},{3,4},{4,5},{5,6}}; + REQUIRE(s0.find(2) == s0.begin() + 1); + REQUIRE(my_as_const(s0).find(3) == s0.cbegin() + 2); + REQUIRE(s0.find(6) == s0.end()); + REQUIRE(my_as_const(s0).find(0) == s0.cend()); + } + { + map_t s0{{1,2},{2,3},{3,4},{4,5},{5,6}}; + REQUIRE(s0.equal_range(3) == std::make_pair(s0.begin() + 2, s0.begin() + 3)); + REQUIRE(s0.equal_range(6) == std::make_pair(s0.end(), s0.end())); + REQUIRE(my_as_const(s0).equal_range(3) == std::make_pair(s0.cbegin() + 2, s0.cbegin() + 3)); + REQUIRE(my_as_const(s0).equal_range(0) == std::make_pair(s0.cbegin(), s0.cbegin())); + } + { + map_t s0{{0,1},{3,2},{6,3}}; + REQUIRE(s0.lower_bound(0) == s0.begin()); + REQUIRE(s0.lower_bound(1) == s0.begin() + 1); + REQUIRE(s0.lower_bound(10) == s0.end()); + REQUIRE(my_as_const(s0).lower_bound(-1) == s0.cbegin()); + REQUIRE(my_as_const(s0).lower_bound(7) == s0.cbegin() + 3); + } } SECTION("observers") { - using map_t = flat_map; - map_t s0; - my_as_const(s0).key_comp(); - my_as_const(s0).value_comp(); + struct my_less { + int i; + my_less(int i) : i(i) {} + bool operator()(int l, int r) const { + return l < r; + } + }; + using map_t = flat_map; + map_t s0(my_less(42)); + REQUIRE(my_as_const(s0).key_comp().i == 42); + REQUIRE(my_as_const(s0).value_comp()({2,50},{4,20})); } SECTION("operators") { using map_t = flat_map; - map_t s0; - map_t s1; - REQUIRE(s0 == s1); - REQUIRE_FALSE(s0 != s1); - REQUIRE_FALSE(s0 < s1); - REQUIRE_FALSE(s0 > s1); - REQUIRE(s0 <= s1); - REQUIRE(s0 >= s1); + + REQUIRE(map_t{{1,2},{3,4}} == map_t{{3,4},{1,2}}); + REQUIRE_FALSE(map_t{{1,2},{3,4}} == map_t{{2,4},{1,2}}); + REQUIRE_FALSE(map_t{{1,2},{3,4}} == map_t{{1,3},{1,2}}); + REQUIRE_FALSE(map_t{{1,2},{3,4}} == map_t{{3,4},{1,2},{0,0}}); + + REQUIRE_FALSE(map_t{{1,2},{3,4}} != map_t{{3,4},{1,2}}); + REQUIRE(map_t{{1,2},{3,4}} != map_t{{2,4},{1,2}}); + REQUIRE(map_t{{1,2},{3,4}} != map_t{{1,3},{1,2}}); + REQUIRE(map_t{{1,2},{3,4}} != map_t{{3,4},{1,2},{0,0}}); + + REQUIRE(map_t{{0,2},{3,4}} < map_t{{1,2},{3,4}}); + REQUIRE(map_t{{1,1},{3,4}} < map_t{{1,2},{3,4}}); + REQUIRE(map_t{{1,2},{3,4}} < map_t{{1,2},{3,4},{5,6}}); + + REQUIRE(map_t{{0,2},{3,4}} <= map_t{{1,2},{3,4}}); + REQUIRE(map_t{{1,1},{3,4}} <= map_t{{1,2},{3,4}}); + REQUIRE(map_t{{1,2},{3,4}} <= map_t{{1,2},{3,4},{5,6}}); + + REQUIRE(map_t{{1,2},{3,4}} > map_t{{0,2},{3,4}}); + REQUIRE(map_t{{1,2},{3,4}} > map_t{{1,1},{3,4}}); + REQUIRE(map_t{{1,2},{3,4},{5,6}} > map_t{{1,2},{3,4}}); + + REQUIRE(map_t{{1,2},{3,4}} >= map_t{{0,2},{3,4}}); + REQUIRE(map_t{{1,2},{3,4}} >= map_t{{1,1},{3,4}}); + REQUIRE(map_t{{1,2},{3,4},{5,6}} >= map_t{{1,2},{3,4}}); + + REQUIRE_FALSE(map_t{{1,2},{3,4}} < map_t{{1,2},{3,4}}); + REQUIRE(map_t{{1,2},{3,4}} <= map_t{{1,2},{3,4}}); + REQUIRE_FALSE(map_t{{1,2},{3,4}} > map_t{{1,2},{3,4}}); + REQUIRE(map_t{{1,2},{3,4}} >= map_t{{1,2},{3,4}}); } }