From 00d676f998132642c172ee85baf90aa9f755be4b Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 27 May 2019 16:51:01 +0700 Subject: [PATCH] heterogeneous find, lower_bound and upper_bound --- .travis.yml | 1 - README.md | 36 +++++++ headers/flat.hpp/detail/is_transparent.hpp | 23 +++++ headers/flat.hpp/detail/pair_compare.hpp | 62 ++++++++++++ headers/flat.hpp/flat_map.hpp | 106 +++++++++++++-------- headers/flat.hpp/flat_multimap.hpp | 106 +++++++++++++-------- headers/flat.hpp/flat_multiset.hpp | 56 +++++++++++ headers/flat.hpp/flat_set.hpp | 56 +++++++++++ untests/flat_map_tests.cpp | 14 +++ untests/flat_multimap_tests.cpp | 14 +++ untests/flat_multiset_tests.cpp | 14 +++ untests/flat_set_tests.cpp | 14 +++ 12 files changed, 419 insertions(+), 83 deletions(-) create mode 100644 headers/flat.hpp/detail/is_transparent.hpp create mode 100644 headers/flat.hpp/detail/pair_compare.hpp diff --git a/.travis.yml b/.travis.yml index 8f3cc8b..1a7301c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,7 +35,6 @@ before_install: - if [ "$TRAVIS_OS_NAME" == 'osx' ]; then brew update; brew upgrade cmake; - brew install git-lfs; fi - if [ "$TRAVIS_OS_NAME" == 'linux' ]; then mkdir $HOME/cmake; diff --git a/README.md b/README.md index 7a1a839..95f119c 100644 --- a/README.md +++ b/README.md @@ -195,14 +195,23 @@ size_type count(const key_type& key) const; iterator find(const key_type& key); const_iterator find(const key_type& key) const; +template < typename K > iterator find(const K& key); +template < typename K > const_iterator find(const K& key) const; + std::pair equal_range(const key_type& key); std::pair equal_range(const key_type& key) const; iterator lower_bound(const key_type& key); const_iterator lower_bound(const key_type& key) const; +template < typename K > iterator lower_bound(const K& key); +template < typename K > const_iterator lower_bound(const K& key) const; + iterator upper_bound(const key_type& key); const_iterator upper_bound(const key_type& key) const; + +template < typename K > iterator upper_bound(const K& key); +template < typename K > const_iterator upper_bound(const K& key) const; ``` ### Observers @@ -426,14 +435,23 @@ size_type count(const key_type& key) const; iterator find(const key_type& key); const_iterator find(const key_type& key) const; +template < typename K > iterator find(const K& key); +template < typename K > const_iterator find(const K& key) const; + std::pair equal_range(const key_type& key); std::pair equal_range(const key_type& key) const; iterator lower_bound(const key_type& key); const_iterator lower_bound(const key_type& key) const; +template < typename K > iterator lower_bound(const K& key); +template < typename K > const_iterator lower_bound(const K& key) const; + iterator upper_bound(const key_type& key); const_iterator upper_bound(const key_type& key) const; + +template < typename K > iterator upper_bound(const K& key); +template < typename K > const_iterator upper_bound(const K& key) const; ``` ### Observers @@ -647,14 +665,23 @@ size_type count(const key_type& key) const; iterator find(const key_type& key); const_iterator find(const key_type& key) const; +template < typename K > iterator find(const K& key); +template < typename K > const_iterator find(const K& key) const; + std::pair equal_range(const key_type& key); std::pair equal_range(const key_type& key) const; iterator lower_bound(const key_type& key); const_iterator lower_bound(const key_type& key) const; +template < typename K > iterator lower_bound(const K& key); +template < typename K > const_iterator lower_bound(const K& key) const; + iterator upper_bound(const key_type& key); const_iterator upper_bound(const key_type& key) const; + +template < typename K > iterator upper_bound(const K& key); +template < typename K > const_iterator upper_bound(const K& key) const; ``` ### Observers @@ -878,14 +905,23 @@ size_type count(const key_type& key) const; iterator find(const key_type& key); const_iterator find(const key_type& key) const; +template < typename K > iterator find(const K& key); +template < typename K > const_iterator find(const K& key) const; + std::pair equal_range(const key_type& key); std::pair equal_range(const key_type& key) const; iterator lower_bound(const key_type& key); const_iterator lower_bound(const key_type& key) const; +template < typename K > iterator lower_bound(const K& key); +template < typename K > const_iterator lower_bound(const K& key) const; + iterator upper_bound(const key_type& key); const_iterator upper_bound(const key_type& key) const; + +template < typename K > iterator upper_bound(const K& key); +template < typename K > const_iterator upper_bound(const K& key) const; ``` ### Observers diff --git a/headers/flat.hpp/detail/is_transparent.hpp b/headers/flat.hpp/detail/is_transparent.hpp new file mode 100644 index 0000000..2f6ec11 --- /dev/null +++ b/headers/flat.hpp/detail/is_transparent.hpp @@ -0,0 +1,23 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/flat.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2019, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include + +namespace flat_hpp::detail +{ + template < typename T, typename U, typename = void > + struct is_transparent + : std::false_type {}; + + template < typename T, typename U > + struct is_transparent> + : std::true_type {}; + + template < typename T, typename U > + inline constexpr bool is_transparent_v = is_transparent::value; +} diff --git a/headers/flat.hpp/detail/pair_compare.hpp b/headers/flat.hpp/detail/pair_compare.hpp new file mode 100644 index 0000000..3d90c44 --- /dev/null +++ b/headers/flat.hpp/detail/pair_compare.hpp @@ -0,0 +1,62 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/flat.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2019, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include "is_transparent.hpp" + +namespace flat_hpp::detail +{ + template < typename Pair, typename Compare > + class pair_compare : public Compare { + public: + pair_compare() = default; + + pair_compare(const Compare& compare) + : Compare(compare) {} + + bool operator()( + const typename Pair::first_type& l, + const typename Pair::first_type& r) const + { + return Compare::operator()(l, r); + } + + bool operator()(const Pair& l, const Pair& r) const { + return Compare::operator()(l.first, r.first); + } + + bool operator()( + const typename Pair::first_type& l, + const Pair& r) const + { + return Compare::operator()(l, r.first); + } + + bool operator()( + const Pair& l, + const typename Pair::first_type& r) const + { + return Compare::operator()(l.first, r); + } + + template < typename K > + std::enable_if_t< + is_transparent_v, + bool> + operator()(const K& l, const Pair& r) const { + return Compare::operator()(l, r.first); + } + + template < typename K > + std::enable_if_t< + is_transparent_v, + bool> + operator()(const Pair& l, const K& r) const { + return Compare::operator()(l.first, r); + } + }; +} diff --git a/headers/flat.hpp/flat_map.hpp b/headers/flat.hpp/flat_map.hpp index 54baa50..c2c3f66 100644 --- a/headers/flat.hpp/flat_map.hpp +++ b/headers/flat.hpp/flat_map.hpp @@ -15,55 +15,21 @@ #include #include +#include "detail/pair_compare.hpp" +#include "detail/is_transparent.hpp" + namespace flat_hpp { - namespace detail - { - template < typename Value, typename Compare > - class flat_map_compare : public Compare { - public: - flat_map_compare() = default; - - flat_map_compare(const Compare& compare) - : Compare(compare) {} - - bool operator()( - const typename Value::first_type& l, - const typename Value::first_type& r) const - { - return Compare::operator()(l, r); - } - - bool operator()( - const typename Value::first_type& l, - const Value& r) const - { - return Compare::operator()(l, r.first); - } - - bool operator()( - const Value& l, - const typename Value::first_type& r) const - { - return Compare::operator()(l.first, r); - } - - bool operator()(const Value& l, const Value& r) const { - return Compare::operator()(l.first, r.first); - } - }; - } - template < typename Key , typename Value , typename Compare = std::less , typename Container = std::vector> > class flat_map - : private detail::flat_map_compare< + : private detail::pair_compare< typename Container::value_type, Compare> { - using base_type = detail::flat_map_compare< + using base_type = detail::pair_compare< typename Container::value_type, Compare>; public: @@ -386,14 +352,36 @@ namespace flat_hpp iterator find(const key_type& key) { const iterator iter = lower_bound(key); - return iter != end() && !this->operator()(key, iter->first) + return iter != end() && !this->operator()(key, *iter) ? iter : end(); } const_iterator find(const key_type& key) const { const const_iterator iter = lower_bound(key); - return iter != end() && !this->operator()(key, iter->first) + return iter != end() && !this->operator()(key, *iter) + ? iter + : end(); + } + + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + iterator> + find(const K& key) { + const iterator iter = lower_bound(key); + return iter != end() && !this->operator()(key, *iter) + ? iter + : end(); + } + + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + const_iterator> + find(const K& key) const { + const const_iterator iter = lower_bound(key); + return iter != end() && !this->operator()(key, *iter) ? iter : end(); } @@ -418,6 +406,24 @@ namespace flat_hpp return std::lower_bound(begin(), end(), key, comp); } + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + iterator> + lower_bound(const K& key) { + const base_type& comp = *this; + return std::lower_bound(begin(), end(), key, comp); + } + + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + const_iterator> + lower_bound(const K& key) const { + const base_type& comp = *this; + return std::lower_bound(begin(), end(), key, comp); + } + iterator upper_bound(const key_type& key) { const base_type& comp = *this; return std::upper_bound(begin(), end(), key, comp); @@ -428,6 +434,24 @@ namespace flat_hpp return std::upper_bound(begin(), end(), key, comp); } + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + iterator> + upper_bound(const K& key) { + const base_type& comp = *this; + return std::upper_bound(begin(), end(), key, comp); + } + + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + const_iterator> + upper_bound(const K& key) const { + const base_type& comp = *this; + return std::upper_bound(begin(), end(), key, comp); + } + key_compare key_comp() const { return *this; } diff --git a/headers/flat.hpp/flat_multimap.hpp b/headers/flat.hpp/flat_multimap.hpp index c20d395..3cd2d5a 100644 --- a/headers/flat.hpp/flat_multimap.hpp +++ b/headers/flat.hpp/flat_multimap.hpp @@ -15,55 +15,21 @@ #include #include +#include "detail/pair_compare.hpp" +#include "detail/is_transparent.hpp" + namespace flat_hpp { - namespace detail - { - template < typename Value, typename Compare > - class flat_multimap_compare : public Compare { - public: - flat_multimap_compare() = default; - - flat_multimap_compare(const Compare& compare) - : Compare(compare) {} - - bool operator()( - const typename Value::first_type& l, - const typename Value::first_type& r) const - { - return Compare::operator()(l, r); - } - - bool operator()( - const typename Value::first_type& l, - const Value& r) const - { - return Compare::operator()(l, r.first); - } - - bool operator()( - const Value& l, - const typename Value::first_type& r) const - { - return Compare::operator()(l.first, r); - } - - bool operator()(const Value& l, const Value& r) const { - return Compare::operator()(l.first, r.first); - } - }; - } - template < typename Key , typename Value , typename Compare = std::less , typename Container = std::vector> > class flat_multimap - : private detail::flat_multimap_compare< + : private detail::pair_compare< typename Container::value_type, Compare> { - using base_type = detail::flat_multimap_compare< + using base_type = detail::pair_compare< typename Container::value_type, Compare>; public: @@ -382,14 +348,36 @@ namespace flat_hpp iterator find(const key_type& key) { const iterator iter = lower_bound(key); - return iter != end() && !this->operator()(key, iter->first) + return iter != end() && !this->operator()(key, *iter) ? iter : end(); } const_iterator find(const key_type& key) const { const const_iterator iter = lower_bound(key); - return iter != end() && !this->operator()(key, iter->first) + return iter != end() && !this->operator()(key, *iter) + ? iter + : end(); + } + + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + iterator> + find(const K& key) { + const iterator iter = lower_bound(key); + return iter != end() && !this->operator()(key, *iter) + ? iter + : end(); + } + + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + const_iterator> + find(const K& key) const { + const const_iterator iter = lower_bound(key); + return iter != end() && !this->operator()(key, *iter) ? iter : end(); } @@ -414,6 +402,24 @@ namespace flat_hpp return std::lower_bound(begin(), end(), key, comp); } + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + iterator> + lower_bound(const K& key) { + const base_type& comp = *this; + return std::lower_bound(begin(), end(), key, comp); + } + + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + const_iterator> + lower_bound(const K& key) const { + const base_type& comp = *this; + return std::lower_bound(begin(), end(), key, comp); + } + iterator upper_bound(const key_type& key) { const base_type& comp = *this; return std::upper_bound(begin(), end(), key, comp); @@ -424,6 +430,24 @@ namespace flat_hpp return std::upper_bound(begin(), end(), key, comp); } + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + iterator> + upper_bound(const K& key) { + const base_type& comp = *this; + return std::upper_bound(begin(), end(), key, comp); + } + + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + const_iterator> + upper_bound(const K& key) const { + const base_type& comp = *this; + return std::upper_bound(begin(), end(), key, comp); + } + key_compare key_comp() const { return *this; } diff --git a/headers/flat.hpp/flat_multiset.hpp b/headers/flat.hpp/flat_multiset.hpp index e6e9111..d361844 100644 --- a/headers/flat.hpp/flat_multiset.hpp +++ b/headers/flat.hpp/flat_multiset.hpp @@ -15,6 +15,8 @@ #include #include +#include "detail/is_transparent.hpp" + namespace flat_hpp { template < typename Key @@ -309,6 +311,28 @@ namespace flat_hpp : end(); } + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + iterator> + find(const K& key) { + const iterator iter = lower_bound(key); + return iter != end() && !this->operator()(key, *iter) + ? iter + : end(); + } + + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + const_iterator> + find(const K& key) const { + const const_iterator iter = lower_bound(key); + return iter != end() && !this->operator()(key, *iter) + ? iter + : end(); + } + std::pair equal_range(const key_type& key) { return std::equal_range(begin(), end(), key, key_comp()); } @@ -325,6 +349,22 @@ namespace flat_hpp return std::lower_bound(begin(), end(), key, key_comp()); } + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + iterator> + lower_bound(const K& key) { + return std::lower_bound(begin(), end(), key, key_comp()); + } + + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + const_iterator> + lower_bound(const K& key) const { + return std::lower_bound(begin(), end(), key, key_comp()); + } + iterator upper_bound(const key_type& key) { return std::upper_bound(begin(), end(), key, key_comp()); } @@ -333,6 +373,22 @@ namespace flat_hpp return std::upper_bound(begin(), end(), key, key_comp()); } + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + iterator> + upper_bound(const K& key) { + return std::upper_bound(begin(), end(), key, key_comp()); + } + + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + const_iterator> + upper_bound(const K& key) const { + return std::upper_bound(begin(), end(), key, key_comp()); + } + key_compare key_comp() const { return *this; } diff --git a/headers/flat.hpp/flat_set.hpp b/headers/flat.hpp/flat_set.hpp index d5cf454..3cdb994 100644 --- a/headers/flat.hpp/flat_set.hpp +++ b/headers/flat.hpp/flat_set.hpp @@ -15,6 +15,8 @@ #include #include +#include "detail/is_transparent.hpp" + namespace flat_hpp { template < typename Key @@ -313,6 +315,28 @@ namespace flat_hpp : end(); } + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + iterator> + find(const K& key) { + const iterator iter = lower_bound(key); + return iter != end() && !this->operator()(key, *iter) + ? iter + : end(); + } + + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + const_iterator> + find(const K& key) const { + const const_iterator iter = lower_bound(key); + return iter != end() && !this->operator()(key, *iter) + ? iter + : end(); + } + std::pair equal_range(const key_type& key) { return std::equal_range(begin(), end(), key, key_comp()); } @@ -329,6 +353,22 @@ namespace flat_hpp return std::lower_bound(begin(), end(), key, key_comp()); } + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + iterator> + lower_bound(const K& key) { + return std::lower_bound(begin(), end(), key, key_comp()); + } + + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + const_iterator> + lower_bound(const K& key) const { + return std::lower_bound(begin(), end(), key, key_comp()); + } + iterator upper_bound(const key_type& key) { return std::upper_bound(begin(), end(), key, key_comp()); } @@ -337,6 +377,22 @@ namespace flat_hpp return std::upper_bound(begin(), end(), key, key_comp()); } + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + iterator> + upper_bound(const K& key) { + return std::upper_bound(begin(), end(), key, key_comp()); + } + + template < typename K > + std::enable_if_t< + detail::is_transparent_v, + const_iterator> + upper_bound(const K& key) const { + return std::upper_bound(begin(), end(), key, key_comp()); + } + key_compare key_comp() const { return *this; } diff --git a/untests/flat_map_tests.cpp b/untests/flat_map_tests.cpp index ced86ce..935b90b 100644 --- a/untests/flat_map_tests.cpp +++ b/untests/flat_map_tests.cpp @@ -9,6 +9,9 @@ #include +#include +#include + #include using namespace flat_hpp; @@ -54,6 +57,10 @@ namespace } TEST_CASE("flat_map") { + SECTION("detail") { + STATIC_REQUIRE(detail::is_transparent, int>::value); + STATIC_REQUIRE_FALSE(detail::is_transparent, int>::value); + } SECTION("sizeof") { REQUIRE(sizeof(flat_map) == sizeof(std::vector>)); @@ -403,6 +410,13 @@ TEST_CASE("flat_map") { REQUIRE(my_as_const(s0).lower_bound(-1) == s0.cbegin()); REQUIRE(my_as_const(s0).lower_bound(7) == s0.cbegin() + 3); } + { + flat_map> s0{{"hello", 42}, {"world", 84}}; + REQUIRE(s0.find(std::string_view("hello")) == s0.begin()); + REQUIRE(my_as_const(s0).find(std::string_view("world")) == s0.begin() + 1); + REQUIRE(s0.find(std::string_view("42")) == s0.end()); + REQUIRE(my_as_const(s0).find(std::string_view("42")) == s0.cend()); + } } SECTION("observers") { struct my_less { diff --git a/untests/flat_multimap_tests.cpp b/untests/flat_multimap_tests.cpp index ccaf5bd..cd97dfa 100644 --- a/untests/flat_multimap_tests.cpp +++ b/untests/flat_multimap_tests.cpp @@ -9,6 +9,9 @@ #include +#include +#include + #include using namespace flat_hpp; @@ -54,6 +57,10 @@ namespace } TEST_CASE("flat_multimap") { + SECTION("detail") { + STATIC_REQUIRE(detail::is_transparent, int>::value); + STATIC_REQUIRE_FALSE(detail::is_transparent, int>::value); + } SECTION("sizeof") { REQUIRE(sizeof(flat_multimap) == sizeof(std::vector>)); @@ -405,6 +412,13 @@ TEST_CASE("flat_multimap") { REQUIRE(my_as_const(s0).lower_bound(-1) == s0.cbegin()); REQUIRE(my_as_const(s0).lower_bound(7) == s0.cbegin() + 4); } + { + flat_multimap> s0{{"hello", 42}, {"world", 84}}; + REQUIRE(s0.find(std::string_view("hello")) == s0.begin()); + REQUIRE(my_as_const(s0).find(std::string_view("world")) == s0.begin() + 1); + REQUIRE(s0.find(std::string_view("42")) == s0.end()); + REQUIRE(my_as_const(s0).find(std::string_view("42")) == s0.cend()); + } } SECTION("observers") { struct my_less { diff --git a/untests/flat_multiset_tests.cpp b/untests/flat_multiset_tests.cpp index 669d799..a5ba1fb 100644 --- a/untests/flat_multiset_tests.cpp +++ b/untests/flat_multiset_tests.cpp @@ -9,6 +9,9 @@ #include +#include +#include + #include using namespace flat_hpp; @@ -54,6 +57,10 @@ namespace } TEST_CASE("flat_multiset") { + SECTION("detail") { + STATIC_REQUIRE(detail::is_transparent, int>::value); + STATIC_REQUIRE_FALSE(detail::is_transparent, int>::value); + } SECTION("sizeof") { REQUIRE(sizeof(flat_multiset) == sizeof(std::vector)); @@ -381,6 +388,13 @@ TEST_CASE("flat_multiset") { REQUIRE(my_as_const(s0).lower_bound(-1) == s0.cbegin()); REQUIRE(my_as_const(s0).lower_bound(7) == s0.cbegin() + 4); } + { + flat_multiset> s0{"hello", "world"}; + REQUIRE(s0.find(std::string_view("hello")) == s0.begin()); + REQUIRE(my_as_const(s0).find(std::string_view("world")) == s0.begin() + 1); + REQUIRE(s0.find(std::string_view("42")) == s0.end()); + REQUIRE(my_as_const(s0).find(std::string_view("42")) == s0.cend()); + } } SECTION("observers") { struct my_less { diff --git a/untests/flat_set_tests.cpp b/untests/flat_set_tests.cpp index 6029598..60f2c52 100644 --- a/untests/flat_set_tests.cpp +++ b/untests/flat_set_tests.cpp @@ -9,6 +9,9 @@ #include +#include +#include + #include using namespace flat_hpp; @@ -54,6 +57,10 @@ namespace } TEST_CASE("flat_set") { + SECTION("detail") { + STATIC_REQUIRE(detail::is_transparent, int>::value); + STATIC_REQUIRE_FALSE(detail::is_transparent, int>::value); + } SECTION("sizeof") { REQUIRE(sizeof(flat_set) == sizeof(std::vector)); @@ -379,6 +386,13 @@ TEST_CASE("flat_set") { REQUIRE(my_as_const(s0).lower_bound(-1) == s0.cbegin()); REQUIRE(my_as_const(s0).lower_bound(7) == s0.cbegin() + 3); } + { + flat_set> s0{"hello", "world"}; + REQUIRE(s0.find(std::string_view("hello")) == s0.begin()); + REQUIRE(my_as_const(s0).find(std::string_view("world")) == s0.begin() + 1); + REQUIRE(s0.find(std::string_view("42")) == s0.end()); + REQUIRE(my_as_const(s0).find(std::string_view("42")) == s0.cend()); + } } SECTION("observers") { struct my_less {