From 69ccde32dd9ec3c981e3eaaf052ed4ee99f9173d Mon Sep 17 00:00:00 2001 From: Marek Gilbert Date: Tue, 23 Jan 2018 22:51:13 -0800 Subject: [PATCH 1/5] Remove -Wconversion which is annoyingly hard to satisfy --- cmake/CompilerSetup.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/CompilerSetup.cmake b/cmake/CompilerSetup.cmake index d026f8221b8..aa4289ac541 100644 --- a/cmake/CompilerSetup.cmake +++ b/cmake/CompilerSetup.cmake @@ -30,7 +30,7 @@ endif() if(CLANG OR GNU) set( common_flags - -Wall -Wextra -Wconversion -Werror + -Wall -Wextra -Werror # Be super pedantic about format strings -Wformat From 42749cedac9f2f8dd1a0fd397d6cebe78260451a Mon Sep 17 00:00:00 2001 From: Marek Gilbert Date: Tue, 23 Jan 2018 22:50:29 -0800 Subject: [PATCH 2/5] Import iterator_adapters from google3 --- .../firebase/firestore/util/CMakeLists.txt | 1 + .../firestore/util/iterator_adaptors.h | 712 ++++++++++ .../firebase/firestore/util/CMakeLists.txt | 4 + .../firestore/util/iterator_adaptors_test.cc | 1236 +++++++++++++++++ 4 files changed, 1953 insertions(+) create mode 100644 Firestore/core/src/firebase/firestore/util/iterator_adaptors.h create mode 100644 Firestore/core/test/firebase/firestore/util/iterator_adaptors_test.cc diff --git a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt index b763a638f30..3e32111a2f6 100644 --- a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt @@ -113,6 +113,7 @@ cc_library( comparison.h config.h firebase_assert.h + iterator_adaptors.h log.h ordered_code.cc ordered_code.h diff --git a/Firestore/core/src/firebase/firestore/util/iterator_adaptors.h b/Firestore/core/src/firebase/firestore/util/iterator_adaptors.h new file mode 100644 index 00000000000..22f45125db0 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/util/iterator_adaptors.h @@ -0,0 +1,712 @@ +/* + * Copyright 2005, 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Provides some iterator adaptors and views. + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_ITERATOR_ADAPTORS_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_ITERATOR_ADAPTORS_H_ + +#include +#include +#include + +#include "absl/base/port.h" +#include "absl/meta/type_traits.h" + +namespace firebase { +namespace firestore { +namespace util { + +namespace internal { + +// value == true if Iter prohibits modification of its pointees. +template +struct IsConstIter + : std::is_const::reference>::type> {}; + +template +struct AddConstIf : std::conditional {}; + +// SynthIterTraits propagates the constness of the 'BaseIter' iterator +// type to its own exported 'pointer' and 'reference' typedefs. +template +struct SynthIterTraits : std::iterator_traits { + private: + static constexpr bool kIterConst = IsConstIter::value; + + public: + using value_type = typename std::remove_cv::type; + using pointer = typename AddConstIf::type*; + using reference = typename AddConstIf::type&; +}; + +// PointeeSynthIterTraits is similar to SynthIterTraits, but the 'Ptr' +// parameter is a pointer-like type, and value_type is the pointee. +template +struct PointeeSynthIterTraits : std::iterator_traits { + private: + static constexpr bool kIterConst = IsConstIter::value; + + public: + using value_type = typename std::pointer_traits::element_type; + using pointer = typename AddConstIf::type*; + using reference = typename AddConstIf::type&; +}; + +// CRTP base class for generating iterator adaptors. +// 'Sub' is the derived type, and 'Policy' encodes +// all of the behavior for the adaptor. +// Policy requirements: +// - type 'underlying_iterator': the underlying iterator type. +// - type 'adapted_traits': the traits of the adaptor. +// - static 'Extract(underlying_iterator)': convert iterator to reference. +// +template +class IteratorAdaptorBase { + private: + // Everything needed from the Policy type is expressed in this section. + using Iterator = typename Policy::underlying_iterator; + using OutTraits = typename Policy::adapted_traits; + static typename OutTraits::reference Extract(const Iterator& it) { + return Policy::Extract(it); + } + + public: + using iterator_category = typename OutTraits::iterator_category; + using value_type = typename OutTraits::value_type; + using pointer = typename OutTraits::pointer; + using reference = typename OutTraits::reference; + using difference_type = typename OutTraits::difference_type; + + IteratorAdaptorBase() : it_() {} + IteratorAdaptorBase(Iterator it) : it_(it) {} // NOLINT(runtime/explicit) + + Sub& sub() { return static_cast(*this); } + const Sub& sub() const { return static_cast(*this); } + + const Iterator& base() const { return it_; } + + reference get() const { return Extract(base()); } + reference operator*() const { return get(); } + pointer operator->() const { return &get(); } + reference operator[](difference_type d) const { return *(sub() + d); } + + Sub& operator++() { + ++it_; + return sub(); + } + Sub& operator--() { + --it_; + return sub(); + } + Sub operator++(int /*unused*/) { return it_++; } + Sub operator--(int /*unused*/) { return it_--; } + + Sub& operator+=(difference_type d) { + it_ += d; + return sub(); + } + Sub& operator-=(difference_type d) { + it_ -= d; + return sub(); + } + + bool operator==(Sub b) const { return base() == b.base(); } + bool operator!=(Sub b) const { return base() != b.base(); } + // These shouldn't be necessary, as implicit conversion from 'Iterator' + // should be enough to make such comparisons work. + bool operator==(Iterator b) const { return *this == Sub(b); } + bool operator!=(Iterator b) const { return *this != Sub(b); } + + friend Sub operator+(Sub it, difference_type d) { return it.base() + d; } + friend Sub operator+(difference_type d, Sub it) { return it + d; } + friend Sub operator-(Sub it, difference_type d) { return it.base() - d; } + friend difference_type operator-(Sub a, Sub b) { return a.base() - b.base(); } + + friend bool operator<(Sub a, Sub b) { return a.base() < b.base(); } + friend bool operator>(Sub a, Sub b) { return a.base() > b.base(); } + friend bool operator<=(Sub a, Sub b) { return a.base() <= b.base(); } + friend bool operator>=(Sub a, Sub b) { return a.base() >= b.base(); } + + private: + Iterator it_; +}; + +template +struct FirstPolicy { + using underlying_iterator = It; + using adapted_traits = + SynthIterTraits::value_type::first_type>; + static typename adapted_traits::reference Extract( + const underlying_iterator& it) { + return it->first; + } +}; + +template +struct SecondPolicy { + using underlying_iterator = It; + using adapted_traits = + SynthIterTraits::value_type::second_type>; + static typename adapted_traits::reference Extract( + const underlying_iterator& it) { + return it->second; + } +}; + +template +struct SecondPtrPolicy { + using underlying_iterator = It; + using adapted_traits = + PointeeSynthIterTraits::value_type::second_type>; + static typename adapted_traits::reference Extract( + const underlying_iterator& it) { + return *it->second; + } +}; + +template +struct PtrPolicy { + using underlying_iterator = It; + using adapted_traits = PointeeSynthIterTraits< + underlying_iterator, + typename std::iterator_traits::value_type>; + static typename adapted_traits::reference Extract( + const underlying_iterator& it) { + return **it; + } +}; + +} // namespace internal + +// In both iterator adaptors, iterator_first<> and iterator_second<>, +// we build a new iterator based on a parameterized iterator type, "It". +// The value type, "Val" is determined by "It::value_type::first" or +// "It::value_type::second", respectively. + +// iterator_first<> adapts an iterator to return the first value of a pair. +// It is equivalent to calling it->first on every value. +// Example: +// +// hash_map values; +// values["foo"] = 1; +// values["bar"] = 2; +// for (iterator_first::iterator> x = values.begin(); +// x != values.end(); ++x) { +// printf("%s", x->c_str()); +// } +template +struct iterator_first + : internal::IteratorAdaptorBase, + internal::FirstPolicy> { + using Base = internal::IteratorAdaptorBase, + internal::FirstPolicy>; + iterator_first() {} + iterator_first(It it) // NOLINT(runtime/explicit) + : Base(it) {} + template + iterator_first(iterator_first o) // NOLINT(runtime/explicit) + : Base(o.base()) {} +}; + +template +iterator_first make_iterator_first(It it) { + return iterator_first(it); +} + +// iterator_second<> adapts an iterator to return the second value of a pair. +// It is equivalent to calling it->second on every value. +// Example: +// +// hash_map values; +// values["foo"] = 1; +// values["bar"] = 2; +// for (iterator_second::iterator> x = values.begin(); +// x != values.end(); ++x) { +// int v = *x; +// printf("%d", v); +// } +template +struct iterator_second + : internal::IteratorAdaptorBase, + internal::SecondPolicy> { + using Base = internal::IteratorAdaptorBase, + internal::SecondPolicy>; + iterator_second() {} + iterator_second(It it) // NOLINT(runtime/explicit) + : Base(it) {} + template + iterator_second(iterator_second o) // NOLINT(runtime/explicit) + : Base(o.base()) {} +}; + +template +iterator_second make_iterator_second(It it) { + return iterator_second(it); +} + +// iterator_second_ptr<> adapts an iterator to return the dereferenced second +// value of a pair. +// It is equivalent to calling *it->second on every value. +// The same result can be achieved by composition +// iterator_ptr > +// Can be used with maps where values are regular pointers or pointers wrapped +// into linked_ptr. This iterator adaptor can be used by classes to give their +// clients access to some of their internal data without exposing too much of +// it. +// +// Example: +// class MyClass { +// public: +// MyClass(const string& s); +// string DebugString() const; +// }; +// typedef hash_map > MyMap; +// typedef iterator_second_ptr MyMapValuesIterator; +// MyMap values; +// values["foo"].reset(new MyClass("foo")); +// values["bar"].reset(new MyClass("bar")); +// for (MyMapValuesIterator it = values.begin(); it != values.end(); ++it) { +// printf("%s", it->DebugString().c_str()); +// } +template +struct iterator_second_ptr + : internal::IteratorAdaptorBase, + internal::SecondPtrPolicy> { + using Base = internal::IteratorAdaptorBase, + internal::SecondPtrPolicy>; + iterator_second_ptr() {} + iterator_second_ptr(It it) // NOLINT(runtime/explicit) + : Base(it) {} + template + iterator_second_ptr(iterator_second_ptr o) // NOLINT(runtime/explicit) + : Base(o.base()) {} +}; + +template +iterator_second_ptr make_iterator_second_ptr(It it) { + return iterator_second_ptr(it); +} + +// iterator_ptr<> adapts an iterator to return the dereferenced value. +// With this adaptor you can write *it instead of **it, or it->something instead +// of (*it)->something. +// Can be used with vectors and lists where values are regular pointers +// or pointers wrapped into linked_ptr. This iterator adaptor can be used by +// classes to give their clients access to some of their internal data without +// exposing too much of it. +// +// Example: +// class MyClass { +// public: +// MyClass(const string& s); +// string DebugString() const; +// }; +// typedef vector > MyVector; +// typedef iterator_ptr DereferencingIterator; +// MyVector values; +// values.push_back(make_linked_ptr(new MyClass("foo"))); +// values.push_back(make_linked_ptr(new MyClass("bar"))); +// for (DereferencingIterator it = values.begin(); it != values.end(); ++it) { +// printf("%s", it->DebugString().c_str()); +// } +// +// Without iterator_ptr you would have to do (*it)->DebugString() +template +struct iterator_ptr : internal::IteratorAdaptorBase, + internal::PtrPolicy> { + using Base = internal::IteratorAdaptorBase, + internal::PtrPolicy>; + iterator_ptr() {} + iterator_ptr(It it) // NOLINT(runtime/explicit) + : Base(it) {} + template + iterator_ptr(iterator_ptr o) // NOLINT(runtime/explicit) + : Base(o.base()) {} +}; + +template +iterator_ptr make_iterator_ptr(It it) { + return iterator_ptr(it); +} + +namespace internal { + +// Template that uses SFINAE to inspect Container abilities: +// . Set has_size_type true, iff T::size_type is defined +// . Define size_type as T::size_type if defined, or size_t otherwise +template +struct container_traits { + private: + // Test for availability of C::size_type. + template + struct test_size_type : std::false_type {}; + template + struct test_size_type> + : std::true_type {}; + + // Conditional provisioning of a size_type which defaults to size_t. + template + struct size_type_def { + using type = typename U::size_type; + }; + template + struct size_type_def { + using type = size_t; + }; + + public: + // Determine whether C::size_type is available. + static const bool has_size_type = test_size_type::value; + + // Provide size_type as either C::size_type if available, or as size_t. + using size_type = typename size_type_def::type; +}; + +template +struct IterGenerator { + using container_type = C; + using iterator = typename C::iterator; + using const_iterator = typename C::const_iterator; + + static iterator begin(container_type& c) { // NOLINT(runtime/references) + return c.begin(); + } + static iterator end(container_type& c) { // NOLINT(runtime/references) + return c.end(); + } + static const_iterator begin(const container_type& c) { return c.begin(); } + static const_iterator end(const container_type& c) { return c.end(); } +}; + +template +struct ReversingIterGeneratorAdaptor { + using container_type = typename SubIterGenerator::container_type; + using iterator = std::reverse_iterator; + using const_iterator = + std::reverse_iterator; + + static iterator begin(container_type& c) { // NOLINT(runtime/references) + return iterator(SubIterGenerator::end(c)); + } + static iterator end(container_type& c) { // NOLINT(runtime/references) + return iterator(SubIterGenerator::begin(c)); + } + static const_iterator begin(const container_type& c) { + return const_iterator(SubIterGenerator::end(c)); + } + static const_iterator end(const container_type& c) { + return const_iterator(SubIterGenerator::begin(c)); + } +}; + +// C: the container type +// Iter: the type of mutable iterator to generate +// ConstIter: the type of constant iterator to generate +// IterGenerator: a policy type that returns native iterators from a C +template > +class iterator_view_helper { + public: + using container_type = C; + using iterator = Iter; + using const_iterator = ConstIter; + using value_type = typename std::iterator_traits::value_type; + using size_type = typename internal::container_traits::size_type; + + explicit iterator_view_helper( + container_type& c) // NOLINT(runtime/references) + : c_(&c) {} + + iterator begin() { return iterator(IterGenerator::begin(container())); } + iterator end() { return iterator(IterGenerator::end(container())); } + const_iterator begin() const { + return const_iterator(IterGenerator::begin(container())); + } + const_iterator end() const { + return const_iterator(IterGenerator::end(container())); + } + const_iterator cbegin() const { return begin(); } + const_iterator cend() const { return end(); } + const container_type& container() const { return *c_; } + container_type& container() { return *c_; } + + bool empty() const { return begin() == end(); } + size_type size() const { return c_->size(); } + + private: + container_type* c_; +}; + +template > +class const_iterator_view_helper { + public: + using container_type = C; + using const_iterator = ConstIter; + using value_type = typename std::iterator_traits::value_type; + using size_type = typename internal::container_traits::size_type; + + explicit const_iterator_view_helper(const container_type& c) : c_(&c) {} + + // Allow implicit conversion from the corresponding iterator_view_helper. + // Erring on the side of constness should be allowed. E.g.: + // MyMap m; + // key_view_type::type keys = key_view(m); // ok + // key_view_type::type const_keys = key_view(m); // ok + template + const_iterator_view_helper( + const iterator_view_helper& v) + : c_(&v.container()) {} + + const_iterator begin() const { + return const_iterator(IterGenerator::begin(container())); + } + const_iterator end() const { + return const_iterator(IterGenerator::end(container())); + } + const_iterator cbegin() const { return begin(); } + const_iterator cend() const { return end(); } + const container_type& container() const { return *c_; } + + bool empty() const { return begin() == end(); } + size_type size() const { return c_->size(); } + + private: + const container_type* c_; +}; + +} // namespace internal + +// Note: The views like value_view, key_view should be in gtl namespace. +// Currently there are lot of callers that reference the methods in the global +// namespace. +// +// Traits to provide a typedef abstraction for the return value +// of the key_view() and value_view() functions, such that +// they can be declared as: +// +// template key_view_t key_view(C& c); +// template value_view_t value_view(C& c); +// +// This abstraction allows callers of these functions to use readable +// type names, and allows the maintainers of iterator_adaptors.h to +// change the return types if needed without updating callers. + +template +struct key_view_type { + using type = internal::iterator_view_helper< + C, iterator_first, + iterator_first>; +}; + +template +struct key_view_type { + using type = internal::const_iterator_view_helper< + C, iterator_first>; +}; + +template +struct value_view_type { + using type = internal::iterator_view_helper< + C, iterator_second, + iterator_second>; +}; + +template +struct value_view_type { + using type = internal::const_iterator_view_helper< + C, iterator_second>; +}; + +// The key_view and value_view functions provide pretty ways to iterate either +// the keys or the values of a map using range based for loops. +// +// Example: +// hash_map my_map; +// ... +// for (string val : value_view(my_map)) { +// ... +// } +// +// Note: If you pass a temporary container to key_view or value_view, be careful +// that the temporary container outlives the wrapper view to avoid dangling +// references. +// This is fine: PublishAll(value_view(Make()); +// This is not: for (const auto& v : value_view(Make())) Publish(v); + +template +typename key_view_type::type key_view( + C& map) { // NOLINT(runtime/references) + return typename key_view_type::type(map); +} + +template +typename key_view_type::type key_view(const C& map) { + return typename key_view_type::type(map); +} + +template +typename value_view_type::type value_view( + C& map) { // NOLINT(runtime/references) + return typename value_view_type::type(map); +} + +template +typename value_view_type::type value_view(const C& map) { + return typename value_view_type::type(map); +} + +// Abstract container view that dereferences the pointer-like .second member +// of a container's std::pair elements, such as the elements of std::map +// or of std::vector>. +// +// Example: +// map elements; +// for (const string& element : deref_second_view(elements)) { +// ... +// } +// +// Note: If you pass a temporary container to deref_second_view, be careful that +// the temporary container outlives the deref_second_view to avoid dangling +// references. +// This is fine: PublishAll(deref_second_view(Make()); +// This is not: for (const auto& v : deref_second_view(Make())) { +// Publish(v); +// } + +template +struct deref_second_view_type { + using type = internal::iterator_view_helper< + C, iterator_second_ptr, + iterator_second_ptr>; +}; + +template +struct deref_second_view_type { + using type = internal::const_iterator_view_helper< + C, iterator_second_ptr>; +}; + +template +typename deref_second_view_type::type deref_second_view( + C& map) { // NOLINT(runtime/references) + return typename deref_second_view_type::type(map); +} + +template +typename deref_second_view_type::type deref_second_view(const C& map) { + return typename deref_second_view_type::type(map); +} + +// Abstract container view that dereferences pointer elements. +// +// Example: +// vector elements; +// for (const string& element : deref_view(elements)) { +// ... +// } +// +// Note: If you pass a temporary container to deref_view, be careful that the +// temporary container outlives the deref_view to avoid dangling references. +// This is fine: PublishAll(deref_view(Make()); +// This is not: for (const auto& v : deref_view(Make())) { Publish(v); } + +template +struct deref_view_type { + using type = internal::iterator_view_helper< + C, iterator_ptr, + iterator_ptr>; +}; + +template +struct deref_view_type { + using type = internal::const_iterator_view_helper< + C, iterator_ptr>; +}; + +template +typename deref_view_type::type deref_view( + C& c) { // NOLINT(runtime/references) + return typename deref_view_type::type(c); +} + +template +typename deref_view_type::type deref_view(const C& c) { + return typename deref_view_type::type(c); +} + +// Abstract container view that iterates backwards. +// +// Example: +// vector elements; +// for (const string& element : reversed_view(elements)) { +// ... +// } +// +// Note: If you pass a temporary container to reversed_view_type, be careful +// that the temporary container outlives the reversed_view to avoid dangling +// references. This is fine: PublishAll(reversed_view(Make()); +// This is not: for (const auto& v : reversed_view(Make())) { Publish(v); } + +template +struct reversed_view_type { + private: + using policy = + internal::ReversingIterGeneratorAdaptor>; + + public: + using type = + internal::iterator_view_helper; +}; + +template +struct reversed_view_type { + private: + using policy = + internal::ReversingIterGeneratorAdaptor>; + + public: + using type = + internal::const_iterator_view_helper; +}; + +template +typename reversed_view_type::type reversed_view( + C& c) { // NOLINT(runtime/references) + return typename reversed_view_type::type(c); +} + +template +typename reversed_view_type::type reversed_view(const C& c) { + return typename reversed_view_type::type(c); +} + +} // namespace util +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_ITERATOR_ADAPTORS_H_ diff --git a/Firestore/core/test/firebase/firestore/util/CMakeLists.txt b/Firestore/core/test/firebase/firestore/util/CMakeLists.txt index 93cca164dff..ed161163eb0 100644 --- a/Firestore/core/test/firebase/firestore/util/CMakeLists.txt +++ b/Firestore/core/test/firebase/firestore/util/CMakeLists.txt @@ -43,11 +43,15 @@ cc_test( autoid_test.cc bits_test.cc comparison_test.cc + iterator_adaptors_test.cc ordered_code_test.cc string_printf_test.cc string_util_test.cc DEPENDS + absl_algorithm + absl_base firebase_firestore_util + gmock ) if(APPLE) diff --git a/Firestore/core/test/firebase/firestore/util/iterator_adaptors_test.cc b/Firestore/core/test/firebase/firestore/util/iterator_adaptors_test.cc new file mode 100644 index 00000000000..6740a23cfb1 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/util/iterator_adaptors_test.cc @@ -0,0 +1,1236 @@ +/* + * Copyright 2005, 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Firestore/core/src/firebase/firestore/util/iterator_adaptors.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/algorithm/container.h" +#include "absl/base/macros.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using std::unordered_map; +using std::unordered_set; + +using testing::ElementsAre; +using testing::Eq; +using testing::IsEmpty; +using testing::Not; +using testing::Pair; +using testing::Pointwise; +using testing::SizeIs; +using firebase::firestore::util::deref_view; +using firebase::firestore::util::deref_second_view; +using firebase::firestore::util::iterator_first; +using firebase::firestore::util::iterator_second; +using firebase::firestore::util::iterator_ptr; +using firebase::firestore::util::iterator_second_ptr; +using firebase::firestore::util::make_iterator_first; +using firebase::firestore::util::make_iterator_second; +using firebase::firestore::util::make_iterator_ptr; +using firebase::firestore::util::make_iterator_second_ptr; +using firebase::firestore::util::key_view; +using firebase::firestore::util::key_view_type; +using firebase::firestore::util::value_view; +using firebase::firestore::util::value_view_type; + +namespace { + +const char * kFirst[] = {"foo", "bar"}; +int kSecond[] = {1, 2}; +const int kCount = ABSL_ARRAYSIZE(kFirst); + +template struct IsConst : std::false_type {}; +template struct IsConst : std::true_type {}; +template struct IsConst : IsConst {}; + +class IteratorAdaptorTest : public testing::Test { + protected: + // Objects declared here can be used by all tests in the test case for Foo. + + virtual void SetUp() { + ASSERT_EQ(ABSL_ARRAYSIZE(kFirst), ABSL_ARRAYSIZE(kSecond)); + } + + virtual void TearDown() { + } + + template + class InlineStorageIter : public std::iterator { + public: + T* operator->() const { return get(); } + T& operator*() const { return *get(); } + + private: + T* get() const { return &v_; } + mutable T v_; + }; + + struct X { int d; }; +}; + + +TEST_F(IteratorAdaptorTest, HashMapFirst) { + // Adapts an iterator to return the first value of a unordered_map::iterator. + typedef unordered_map my_container; + my_container values; + for (int i = 0; i < kCount; ++i) { + values[kFirst[i]] = kSecond[i]; + } + for (iterator_first it = values.begin(); + it != values.end(); ++it) { + ASSERT_GT(it->length(), 0u); + } +} + +TEST_F(IteratorAdaptorTest, IteratorPtrUniquePtr) { + // Tests iterator_ptr with a vector>. + typedef std::vector> my_container; + typedef iterator_ptr my_iterator; + my_container values; + for (int i = 0; i < kCount; ++i) { + values.push_back(std::unique_ptr(new int(kSecond[i]))); + } + int i = 0; + for (my_iterator it = values.begin(); it != values.end(); ++it, ++i) { + int v = *it; + *it = v; + ASSERT_EQ(v, kSecond[i]); + } +} + +TEST_F(IteratorAdaptorTest, IteratorFirstConvertsToConst) { + // Adapts an iterator to return the first value of a unordered_map::iterator. + typedef unordered_map my_container; + my_container values; + for (int i = 0; i < kCount; ++i) { + values[kFirst[i]] = kSecond[i]; + } + iterator_first iter = values.begin(); + iterator_first c_iter = iter; + for (; c_iter != values.end(); ++c_iter) { + ASSERT_GT(c_iter->length(), 0u); + } +} + +TEST_F(IteratorAdaptorTest, IteratorFirstConstEqNonConst) { + // verify that const and non-const iterators return the same reference. + typedef std::vector > my_container; + typedef iterator_first my_iterator; + typedef iterator_first my_const_iterator; + my_container values; + for (int i = 0; i < kCount; ++i) { + values.push_back(std::make_pair(i, i + 1)); + } + my_iterator iter1 = values.begin(); + const my_iterator iter2 = iter1; + my_const_iterator c_iter1 = iter1; + const my_const_iterator c_iter2 = c_iter1; + for (int i = 0; i < kCount; ++i) { + int& v1 = iter1[i]; + int& v2 = iter2[i]; + EXPECT_EQ(&v1, &values[i].first); + EXPECT_EQ(&v1, &v2); + const int& cv1 = c_iter1[i]; + const int& cv2 = c_iter2[i]; + EXPECT_EQ(&cv1, &values[i].first); + EXPECT_EQ(&cv1, &cv2); + } +} + +TEST_F(IteratorAdaptorTest, HashMapSecond) { + // Adapts an iterator to return the second value of a unordered_map::iterator. + typedef unordered_map my_container; + my_container values; + for (int i = 0; i < kCount; ++i) { + values[kFirst[i]] = kSecond[i]; + } + for (iterator_second it = values.begin(); + it != values.end(); ++it) { + int v = *it; + ASSERT_GT(v, 0); + } +} + +TEST_F(IteratorAdaptorTest, IteratorSecondConvertsToConst) { + // Adapts an iterator to return the first value of a unordered_map::iterator. + typedef unordered_map my_container; + my_container values; + for (int i = 0; i < kCount; ++i) { + values[kFirst[i]] = kSecond[i]; + } + iterator_second iter = values.begin(); + iterator_second c_iter = iter; + for (; c_iter != values.end(); ++c_iter) { + int v = *c_iter; + ASSERT_GT(v, 0); + } +} + +TEST_F(IteratorAdaptorTest, IteratorSecondConstEqNonConst) { + // verify that const and non-const iterators return the same reference. + typedef std::vector > my_container; + typedef iterator_second my_iterator; + typedef iterator_second my_const_iterator; + my_container values; + for (int i = 0; i < kCount; ++i) { + values.push_back(std::make_pair(i, i + 1)); + } + my_iterator iter1 = values.begin(); + const my_iterator iter2 = iter1; + my_const_iterator c_iter1 = iter1; + const my_const_iterator c_iter2 = c_iter1; + for (int i = 0; i < kCount; ++i) { + int& v1 = iter1[i]; + int& v2 = iter2[i]; + EXPECT_EQ(&v1, &values[i].second); + EXPECT_EQ(&v1, &v2); + const int& cv1 = c_iter1[i]; + const int& cv2 = c_iter2[i]; + EXPECT_EQ(&cv1, &values[i].second); + EXPECT_EQ(&cv1, &cv2); + } +} + +TEST_F(IteratorAdaptorTest, IteratorSecondPtrConvertsToConst) { + // Adapts an iterator to return the first value of a unordered_map::iterator. + typedef unordered_map my_container; + my_container values; + for (int i = 0; i < kCount; ++i) { + values[kFirst[i]] = &kSecond[i]; + } + iterator_second_ptr iter = values.begin(); + iterator_second_ptr c_iter = iter; + for (; c_iter != values.end(); ++c_iter) { + int v = *c_iter; + ASSERT_GT(v, 0); + } +} + +TEST_F(IteratorAdaptorTest, IteratorSecondPtrConstMap) { + typedef const std::map ConstMap; + ConstMap empty_map; + + iterator_second_ptr it(empty_map.begin()); + ASSERT_TRUE(it == make_iterator_second_ptr(empty_map.end())); + if ((false)) { + // Just checking syntax/compilation/type-checking. + // iterator_second_ptr::value_type* v1 = &*it; + iterator_second_ptr::pointer v1 = &*it; + iterator_second_ptr::pointer v2 = + &*it.operator->(); + if (&v1 != &v2) v1 = v2; + } +} + +TEST_F(IteratorAdaptorTest, IteratorPtrConst) { + // This is a regression test for a const-related bug that bit CL 47984515, + // where a client created an iterator whose value type was "T* const". + std::map m; + make_iterator_ptr(make_iterator_first(m.begin())); +} + +TEST_F(IteratorAdaptorTest, IteratorSecondPtrConstEqNonConst) { + // verify that const and non-const iterators return the same reference. + typedef std::vector > my_container; + typedef iterator_second_ptr my_iterator; + typedef iterator_second_ptr my_const_iterator; + my_container values; + int ivalues[kCount]; + for (int i = 0; i < kCount; ++i) { + ivalues[i] = i; + values.push_back(std::make_pair(i, &ivalues[i])); + } + my_iterator iter1 = values.begin(); + const my_iterator iter2 = iter1; + my_const_iterator c_iter1 = iter1; + const my_const_iterator c_iter2 = c_iter1; + for (int i = 0; i < kCount; ++i) { + int& v1 = iter1[i]; + int& v2 = iter2[i]; + EXPECT_EQ(&v1, &ivalues[i]); + EXPECT_EQ(&v1, &v2); + const int& cv1 = c_iter1[i]; + const int& cv2 = c_iter2[i]; + EXPECT_EQ(&cv1, &ivalues[i]); + EXPECT_EQ(&cv1, &cv2); + } +} + +TEST_F(IteratorAdaptorTest, HashMapFirstConst) { + // Adapts an iterator to return the first value of a + // unordered_map::const_iterator. + typedef unordered_map my_container; + my_container values; + for (int i = 0; i < kCount; ++i) { + values[kFirst[i]] = kSecond[i]; + } + const unordered_map* cvalues = &values; + for (iterator_first it = cvalues->begin(); + it != cvalues->end(); ++it) { + ASSERT_GT(it->length(), 0u); + } +} + +TEST_F(IteratorAdaptorTest, ListFirst) { + // Adapts an iterator to return the first value of a list::iterator. + typedef std::pair my_pair; + typedef std::list my_list; + my_list values; + for (int i = 0; i < kCount; ++i) { + values.push_back(my_pair(kFirst[i], kSecond[i])); + } + int i = 0; + for (iterator_first it = values.begin(); + it != values.end(); ++it) { + ASSERT_EQ(*it, kFirst[i++]); + } +} + +TEST_F(IteratorAdaptorTest, ListSecondConst) { + // Adapts an iterator to return the second value from a list::const_iterator. + typedef std::pair my_pair; + typedef std::list my_list; + my_list values; + for (int i = 0; i < kCount; ++i) { + values.push_back(my_pair(kFirst[i], kSecond[i])); + } + int i = 0; + const my_list* cvalues = &values; + for (iterator_second it = cvalues->begin(); + it != cvalues->end(); ++it) { + ASSERT_EQ(*it, kSecond[i++]); + } +} + +TEST_F(IteratorAdaptorTest, VectorSecond) { + // Adapts an iterator to return the second value of a vector::iterator. + std::vector > values; + for (int i = 0; i < kCount; ++i) { + values.push_back(std::pair(kFirst[i], kSecond[i])); + } + int i = 0; + for (iterator_second< std::vector >::iterator > + it = values.begin(); + it != values.end(); ++it) { + ASSERT_EQ(*it, kSecond[i++]); + } +} + + +// Tests iterator_second_ptr with a map where values are regular pointers. +TEST_F(IteratorAdaptorTest, HashMapSecondPtr) { + typedef unordered_map my_container; + typedef iterator_second_ptr my_iterator; + my_container values; + for (int i = 0; i < kCount; ++i) { + values[kFirst[i]] = kSecond + i; + } + for (my_iterator it = values.begin(); it != values.end(); ++it) { + int v = *it; + + // Make sure the iterator reference type is assignable ("int&" and not + // "const int&"). If it isn't, this becomes a compile-time error. + *it = v; + + ASSERT_GT(v, 0); + } +} + +// Tests iterator_second_ptr with a map where values are wrapped into +// linked_ptr. +TEST_F(IteratorAdaptorTest, HashMapSecondPtrLinkedPtr) { + typedef unordered_map > my_container; + typedef iterator_second_ptr my_iterator; + my_container values; + for (int i = 0; i < kCount; ++i) { + values[kFirst[i]].reset(new int(kSecond[i])); + } + for (my_iterator it = values.begin(); it != values.end(); ++it) { + ASSERT_EQ(&*it, it.operator->()); + int v = *it; + *it = v; + ASSERT_GT(v, 0); + } +} + +// Tests iterator_ptr with a vector where values are regular pointers. +TEST_F(IteratorAdaptorTest, IteratorPtrPtr) { + typedef std::vector my_container; + typedef iterator_ptr my_iterator; + my_container values; + for (int i = 0; i < kCount; ++i) { + values.push_back(kSecond + i); + } + int i = 0; + for (my_iterator it = values.begin(); it != values.end(); ++it, ++i) { + int v = *it; + *it = v; + ASSERT_EQ(v, kSecond[i]); + } +} + +TEST_F(IteratorAdaptorTest, IteratorPtrExplicitPtrType) { + struct A {}; + struct B : A {}; + std::vector v; + const std::vector& cv = v; + iterator_ptr::iterator, A*> ip(v.begin()); + iterator_ptr::const_iterator, A*> cip(cv.begin()); +} + +TEST_F(IteratorAdaptorTest, IteratorPtrtConstEqNonConst) { + // verify that const and non-const iterators return the same reference. + typedef std::vector my_container; + typedef iterator_ptr my_iterator; + typedef iterator_ptr my_const_iterator; + my_container values; + + for (int i = 0; i < kCount; ++i) { + values.push_back(kSecond + i); + } + my_iterator iter1 = values.begin(); + const my_iterator iter2 = iter1; + my_const_iterator c_iter1 = iter1; + const my_const_iterator c_iter2 = iter1; + for (int i = 0; i < kCount; ++i) { + int &v1 = iter1[i]; + int &v2 = iter2[i]; + EXPECT_EQ(&v1, kSecond + i); + EXPECT_EQ(&v1, &v2); + const int& cv1 = c_iter1[i]; + const int& cv2 = c_iter2[i]; + EXPECT_EQ(&cv1, kSecond + i); + EXPECT_EQ(&cv1, &cv2); + } +} + +// Tests iterator_ptr with a vector where values are wrapped into std::shared_ptr. +TEST_F(IteratorAdaptorTest, IteratorPtrLinkedPtr) { + typedef std::vector > my_container; + typedef iterator_ptr my_iterator; + my_container values; + for (int i = 0; i < kCount; ++i) { + values.push_back(std::make_shared(kSecond[i])); + } + int i = 0; + for (my_iterator it = values.begin(); it != values.end(); ++it, ++i) { + ASSERT_EQ(&*it, it.operator->()); + int v = *it; + *it = v; + ASSERT_EQ(v, kSecond[i]); + } +} + +TEST_F(IteratorAdaptorTest, IteratorPtrConvertsToConst) { + int value = 1; + std::vector values; + values.push_back(&value); + iterator_ptr::iterator> iter = values.begin(); + iterator_ptr::const_iterator> c_iter = iter; + EXPECT_EQ(1, *c_iter); +} + +TEST_F(IteratorAdaptorTest, IteratorFirstHasRandomAccessMethods) { + typedef std::vector > my_container; + typedef iterator_first my_iterator; + + my_container values; + for (int i = 0; i < kCount; ++i) { + values.push_back(std::pair(kFirst[i], kSecond[i])); + } + + my_iterator it1 = values.begin(), it2 = values.end(); + + EXPECT_EQ(kCount, it2 - it1); + EXPECT_TRUE(it1 < it2); + it1 += kCount; + EXPECT_TRUE(it1 == it2); + it1 -= kCount; + EXPECT_EQ(kFirst[0], *it1); + EXPECT_EQ(kFirst[1], *(it1 + 1)); + EXPECT_TRUE(it1 == it2 - kCount); + EXPECT_TRUE(kCount + it1 == it2); + EXPECT_EQ(kFirst[1], it1[1]); + it2[-1] = "baz"; + EXPECT_EQ("baz", values[kCount - 1].first); +} + +TEST_F(IteratorAdaptorTest, IteratorSecondHasRandomAccessMethods) { + typedef std::vector > my_container; + typedef iterator_second my_iterator; + + my_container values; + for (int i = 0; i < kCount; ++i) { + values.push_back(std::pair(kFirst[i], kSecond[i])); + } + + my_iterator it1 = values.begin(), it2 = values.end(); + + EXPECT_EQ(kCount, it2 - it1); + EXPECT_TRUE(it1 < it2); + it1 += kCount; + EXPECT_TRUE(it1 == it2); + it1 -= kCount; + EXPECT_EQ(kSecond[0], *it1); + EXPECT_EQ(kSecond[1], *(it1 + 1)); + EXPECT_TRUE(it1 == it2 - kCount); + EXPECT_TRUE(kCount + it1 == it2); + EXPECT_EQ(kSecond[1], it1[1]); + it2[-1] = 99; + EXPECT_EQ(99, values[kCount - 1].second); +} + +TEST_F(IteratorAdaptorTest, IteratorSecondPtrHasRandomAccessMethods) { + typedef std::vector > my_container; + typedef iterator_second_ptr my_iterator; + + ASSERT_GE(kCount, 2); + int value1 = 17; + int value2 = 99; + my_container values; + values.push_back(std::pair(kFirst[0], &value1)); + values.push_back(std::pair(kFirst[1], &value2)); + + my_iterator it1 = values.begin(), it2 = values.end(); + + EXPECT_EQ(2, it2 - it1); + EXPECT_TRUE(it1 < it2); + it1 += 2; + EXPECT_TRUE(it1 == it2); + it1 -= 2; + EXPECT_EQ(17, *it1); + EXPECT_EQ(99, *(it1 + 1)); + EXPECT_TRUE(it1 == it2 - 2); + EXPECT_TRUE(2 + it1 == it2); + EXPECT_EQ(99, it1[1]); + it2[-1] = 88; + EXPECT_EQ(88, value2); +} + +TEST_F(IteratorAdaptorTest, IteratorPtrHasRandomAccessMethods) { + typedef std::vector my_container; + typedef iterator_ptr my_iterator; + + int value1 = 17; + int value2 = 99; + my_container values; + values.push_back(&value1); + values.push_back(&value2); + + my_iterator it1 = values.begin(), it2 = values.end(); + + EXPECT_EQ(2, it2 - it1); + EXPECT_TRUE(it1 < it2); + it1 += 2; + EXPECT_TRUE(it1 == it2); + it1 -= 2; + EXPECT_EQ(17, *it1); + EXPECT_EQ(99, *(it1 + 1)); + EXPECT_TRUE(it1 == it2 - 2); + EXPECT_TRUE(2 + it1 == it2); + EXPECT_EQ(99, it1[1]); + it2[-1] = 88; + EXPECT_EQ(88, value2); +} + +class MyInputIterator : + public std::iterator { + public: + explicit MyInputIterator(int* x) : x_(x) {} + const int* operator*() const { return x_; } + MyInputIterator& operator++() { ++*x_; return *this; } + + private: + int *x_; +}; + +TEST_F(IteratorAdaptorTest, IteratorPtrCanWrapInputIterator) { + int x = 0; + MyInputIterator it(&x); + iterator_ptr it1(it); + + EXPECT_EQ(0, *it1); ++it1; + EXPECT_EQ(1, *it1); ++it1; + EXPECT_EQ(2, *it1); ++it1; +} + +// Tests that a default-constructed adaptor is equal to an adaptor explicitly +// constructed with a default underlying iterator. +TEST_F(IteratorAdaptorTest, DefaultAdaptorConstructorUsesDefaultValue) { + iterator_first*> first_default; + iterator_first*> first_null(nullptr); + ASSERT_TRUE(first_default == first_null); + + iterator_second*> second_default; + iterator_second*> second_null(nullptr); + ASSERT_TRUE(second_default == second_null); + + iterator_second_ptr*> second_ptr_default; + iterator_second_ptr*> second_ptr_null(nullptr); + ASSERT_TRUE(second_ptr_default == second_ptr_null); + + iterator_ptr ptr_default; + iterator_ptr ptr_null(nullptr); + ASSERT_TRUE(ptr_default == ptr_null); +} + +// Non C++11 test. We use c_copy() to avoid having to spell out the types. +TEST_F(IteratorAdaptorTest, ValueView) { + typedef unordered_map MapType; + MapType my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + const MapType c_map(my_map); + + std::set vals; + absl::c_copy(value_view(c_map), inserter(vals, vals.end())); + + EXPECT_THAT(vals, ElementsAre("a", "b", "c")); +} + +TEST_F(IteratorAdaptorTest, ValueView_Modify) { + typedef std::map MapType; + MapType my_map; + my_map[0] = 0; + my_map[1] = 1; + my_map[2] = 2; + EXPECT_THAT(my_map, ElementsAre(Pair(0, 0), Pair(1, 1), Pair(2, 2))); + + value_view_type::type vv = value_view(my_map); + absl::c_replace(vv, 2, 3); + absl::c_replace(vv, 1, 2); + + EXPECT_THAT(my_map, ElementsAre(Pair(0, 0), Pair(1, 2), Pair(2, 3))); +} + +TEST_F(IteratorAdaptorTest, ValueViewOfValueView) { + typedef std::pair pair_int_str; + typedef std::map map_int_pair_int_str; + map_int_pair_int_str my_map; + my_map[0] = std::make_pair(1, std::string("a")); + my_map[2] = std::make_pair(3, std::string("b")); + my_map[4] = std::make_pair(5, std::string("c")); + + // This is basically typechecking of the generated views. So we generate the + // types and have the compiler verify the generated template instantiation. + typedef value_view_type::type + value_view_map_int_pair_int_str_type; + + static_assert( + (std::is_same::value), + "value_view_value_type_"); + + typedef value_view_type::type + view_view_type; + + static_assert( + (std::is_same::value), + "view_view_type_"); + + value_view_map_int_pair_int_str_type vv = value_view(my_map); + view_view_type helper = value_view(vv); + + EXPECT_THAT(std::set(helper.begin(), helper.end()), + ElementsAre("a", "b", "c")); +} + +TEST_F(IteratorAdaptorTest, ValueViewAndKeyViewCopy) { + std::map my_map; + my_map[0] = "0"; + my_map[1] = "1"; + my_map[2] = "2"; + std::set keys; + std::set vals; + absl::c_copy(key_view(my_map), inserter(keys, keys.end())); + absl::c_copy(value_view(my_map), inserter(vals, vals.end())); + EXPECT_THAT(keys, ElementsAre(0, 1, 2)); + EXPECT_THAT(vals, ElementsAre("0", "1", "2")); +} + +TEST_F(IteratorAdaptorTest, ValueViewAndKeyViewRangeBasedLoop) { + std::map my_map; + my_map[0] = "0"; + my_map[1] = "1"; + my_map[2] = "2"; + std::set keys; + std::set vals; + for (auto key : key_view(my_map)) { + keys.insert(key); + } + for (auto val : value_view(my_map)) { + vals.insert(val); + } + EXPECT_THAT(keys, ElementsAre(0, 1, 2)); + EXPECT_THAT(vals, ElementsAre("0", "1", "2")); +} + +template +class FixedSizeContainer { + public: + // NOTE: the container does on purpose not define: + // reference, const_reference, pointer, const_pointer, size_type, + // difference_type, empty(). + typedef std::pair value_type; + typedef value_type* iterator; + typedef const value_type* const_iterator; + + FixedSizeContainer() {} + const_iterator begin() const { return &values[0]; } + iterator begin() { return &values[0]; } + const_iterator end() const { return &values[N]; } + iterator end() { return &values[N]; } + value_type at(int n) const { return values[n]; } + value_type& operator[](int n) { return values[n]; } + int size() const { return N; } + + private: + value_type values[N ? N : 1]; + // NOTE: the container does on purpose not define: + // reference, const_reference, pointer, const_pointer, size_type, + // difference_type, empty(). +}; + +TEST_F(IteratorAdaptorTest, ProvidesEmpty) { + { + FixedSizeContainer<0, int, int> container0; + EXPECT_TRUE(value_view(container0).empty()); + FixedSizeContainer<1, int, int> container1; + EXPECT_FALSE(value_view(container1).empty()); + } + { + std::map container; + EXPECT_TRUE(value_view(container).empty()); + container.insert(std::make_pair(0, 0)); + EXPECT_FALSE(value_view(container).empty()); + } +} + +TEST_F(IteratorAdaptorTest, ValueViewWithPoorlyTypedHomeGrownContainer) { + FixedSizeContainer<3, int, std::string> container; + container[0] = std::make_pair(0, std::string("0")); + container[1] = std::make_pair(1, std::string("1")); + container[2] = std::make_pair(2, std::string("2")); + EXPECT_EQ(3, container.size()); + EXPECT_EQ(container.at(0), std::make_pair(0, std::string("0"))); + EXPECT_EQ(container.at(1), std::make_pair(1, std::string("1"))); + EXPECT_EQ(container.at(2), std::make_pair(2, std::string("2"))); + std::vector keys; + std::vector vals; + absl::c_copy(key_view(container), back_inserter(keys)); + absl::c_copy(value_view(container), back_inserter(vals)); + EXPECT_THAT(keys, ElementsAre(0, 1, 2)); + EXPECT_THAT(vals, ElementsAre("0", "1", "2")); +} + +TEST_F(IteratorAdaptorTest, ValueViewConstIterators) { + unordered_map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + std::set vals; + // iterator_view_helper defines cbegin() and cend(); we're not invoking the + // C++11 functions of the same name. + for (iterator_second::const_iterator> it = + value_view(my_map).cbegin(); + it != value_view(my_map).cend(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find("a") != vals.end()); + EXPECT_TRUE(vals.find("b") != vals.end()); + EXPECT_TRUE(vals.find("c") != vals.end()); +} + +TEST_F(IteratorAdaptorTest, ValueViewInConstContext) { + using firebase::firestore::util::internal::iterator_view_helper; + unordered_map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + std::set vals; + const iterator_view_helper< + unordered_map, + iterator_second::iterator>, + iterator_second::const_iterator> > const_view = + value_view(my_map); + for (iterator_second::const_iterator> it = + const_view.begin(); + it != const_view.end(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find("a") != vals.end()); + EXPECT_TRUE(vals.find("b") != vals.end()); + EXPECT_TRUE(vals.find("c") != vals.end()); +} + +TEST_F(IteratorAdaptorTest, ConstValueView) { + unordered_map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + const unordered_map& const_map = my_map; + + std::set vals; + for (iterator_second::const_iterator> it = + value_view(const_map).begin(); + it != value_view(const_map).end(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find("a") != vals.end()); + EXPECT_TRUE(vals.find("b") != vals.end()); + EXPECT_TRUE(vals.find("c") != vals.end()); +} + +TEST_F(IteratorAdaptorTest, ConstValueViewConstIterators) { + unordered_map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + const unordered_map& const_map = my_map; + + std::set vals; + // iterator_view_helper defines cbegin() and cend(); we're not invoking the + // C++11 functions of the same name. + for (iterator_second::const_iterator> it = + value_view(const_map).cbegin(); + it != value_view(const_map).cend(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find("a") != vals.end()); + EXPECT_TRUE(vals.find("b") != vals.end()); + EXPECT_TRUE(vals.find("c") != vals.end()); +} + +TEST_F(IteratorAdaptorTest, ConstValueViewInConstContext) { + unordered_map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + const unordered_map& const_map = my_map; + + std::set vals; + const value_view_type >::type const_view = + value_view(const_map); + for (iterator_second::const_iterator> it = + const_view.begin(); + it != const_view.end(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find("a") != vals.end()); + EXPECT_TRUE(vals.find("b") != vals.end()); + EXPECT_TRUE(vals.find("c") != vals.end()); +} + +TEST_F(IteratorAdaptorTest, KeyView) { + unordered_map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + std::set vals; + for (iterator_first::iterator> it = + key_view(my_map).begin(); + it != key_view(my_map).end(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find(0) != vals.end()); + EXPECT_TRUE(vals.find(1) != vals.end()); + EXPECT_TRUE(vals.find(2) != vals.end()); +} + +TEST_F(IteratorAdaptorTest, KeyViewConstIterators) { + unordered_map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + std::set vals; + // iterator_view_helper defines cbegin() and cend(); we're not invoking the + // C++11 functions of the same name. + for (iterator_first::const_iterator> it = + key_view(my_map).cbegin(); + it != key_view(my_map).cend(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find(0) != vals.end()); + EXPECT_TRUE(vals.find(1) != vals.end()); + EXPECT_TRUE(vals.find(2) != vals.end()); +} + +TEST_F(IteratorAdaptorTest, KeyViewInConstContext) { + unordered_map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + std::set vals; + const key_view_type >::type const_view = + key_view(my_map); + for (iterator_first::const_iterator> it = + const_view.begin(); + it != const_view.end(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find(0) != vals.end()); + EXPECT_TRUE(vals.find(1) != vals.end()); + EXPECT_TRUE(vals.find(2) != vals.end()); +} + +TEST_F(IteratorAdaptorTest, ConstKeyView) { + unordered_map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + const unordered_map& const_map = my_map; + + std::set vals; + for (iterator_first::const_iterator> it = + key_view(const_map).begin(); + it != key_view(const_map).end(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find(0) != vals.end()); + EXPECT_TRUE(vals.find(1) != vals.end()); + EXPECT_TRUE(vals.find(2) != vals.end()); +} + +TEST_F(IteratorAdaptorTest, ConstKeyViewConstIterators) { + unordered_map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + const unordered_map& const_map = my_map; + + std::set vals; + // iterator_view_helper defines cbegin() and cend(); we're not invoking the + // C++11 functions of the same name. + for (iterator_first::const_iterator> it = + key_view(const_map).cbegin(); + it != key_view(const_map).cend(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find(0) != vals.end()); + EXPECT_TRUE(vals.find(1) != vals.end()); + EXPECT_TRUE(vals.find(2) != vals.end()); +} + +TEST_F(IteratorAdaptorTest, ConstKeyViewInConstContext) { + unordered_map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + const unordered_map& const_map = my_map; + + std::set vals; + const key_view_type >::type const_view = + key_view(const_map); + for (iterator_first::const_iterator> it = + const_view.begin(); + it != const_view.end(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find(0) != vals.end()); + EXPECT_TRUE(vals.find(1) != vals.end()); + EXPECT_TRUE(vals.find(2) != vals.end()); +} + +TEST_F(IteratorAdaptorTest, IteratorViewHelperDefinesIterator) { + using firebase::firestore::util::internal::iterator_view_helper; + unordered_set my_set; + my_set.insert(1); + my_set.insert(0); + my_set.insert(2); + + typedef iterator_view_helper, + unordered_set::iterator, + unordered_set::const_iterator> SetView; + SetView set_view(my_set); + unordered_set vals; + for (SetView::iterator it = set_view.begin(); it != set_view.end(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find(0) != vals.end()); + EXPECT_TRUE(vals.find(1) != vals.end()); + EXPECT_TRUE(vals.find(2) != vals.end()); +} + +TEST_F(IteratorAdaptorTest, IteratorViewHelperDefinesConstIterator) { + using firebase::firestore::util::internal::iterator_view_helper; + unordered_set my_set; + my_set.insert(1); + my_set.insert(0); + my_set.insert(2); + + typedef iterator_view_helper, + unordered_set::iterator, + unordered_set::const_iterator> SetView; + SetView set_view(my_set); + unordered_set vals; + for (SetView::const_iterator it = set_view.begin(); + it != set_view.end(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find(0) != vals.end()); + EXPECT_TRUE(vals.find(1) != vals.end()); + EXPECT_TRUE(vals.find(2) != vals.end()); +} + +TEST_F(IteratorAdaptorTest, ViewTypeParameterConstVsNonConst) { + typedef unordered_map M; + M m; + const M& cm = m; + + typedef key_view_type::type KV; + typedef key_view_type::type KVC; + typedef value_view_type::type VV; + typedef value_view_type::type VVC; + + // key_view: + KV ABSL_ATTRIBUTE_UNUSED kv1 = key_view(m); // lvalue + KVC ABSL_ATTRIBUTE_UNUSED kv2 = key_view(m); // conversion to const + KVC ABSL_ATTRIBUTE_UNUSED kv3 = key_view(cm); // const from const lvalue + KVC ABSL_ATTRIBUTE_UNUSED kv4 = key_view(M()); // const from rvalue + // Direct initialization (without key_view function) + KV ABSL_ATTRIBUTE_UNUSED kv5(m); + KVC ABSL_ATTRIBUTE_UNUSED kv6(m); + KVC ABSL_ATTRIBUTE_UNUSED kv7(cm); + KVC ABSL_ATTRIBUTE_UNUSED kv8((M())); + + // value_view: + VV ABSL_ATTRIBUTE_UNUSED vv1 = value_view(m); // lvalue + VVC ABSL_ATTRIBUTE_UNUSED vv2 = value_view(m); // conversion to const + VVC ABSL_ATTRIBUTE_UNUSED vv3 = value_view(cm); // const from const lvalue + VVC ABSL_ATTRIBUTE_UNUSED vv4 = value_view(M()); // const from rvalue + // Direct initialization (without value_view function) + VV ABSL_ATTRIBUTE_UNUSED vv5(m); + VVC ABSL_ATTRIBUTE_UNUSED vv6(m); + VVC ABSL_ATTRIBUTE_UNUSED vv7(cm); + VVC ABSL_ATTRIBUTE_UNUSED vv8((M())); +} + +TEST_F(IteratorAdaptorTest, EmptyAndSize) { + { + FixedSizeContainer<0, int, std::string*> container; + EXPECT_TRUE(key_view(container).empty()); + EXPECT_TRUE(value_view(container).empty()); + EXPECT_EQ(0u, key_view(container).size()); + EXPECT_EQ(0u, value_view(container).size()); + } + { + FixedSizeContainer<2, int, std::string*> container; + EXPECT_FALSE(key_view(container).empty()); + EXPECT_FALSE(value_view(container).empty()); + EXPECT_EQ(2u, key_view(container).size()); + EXPECT_EQ(2u, value_view(container).size()); + } + { + std::map container; + EXPECT_TRUE(key_view(container).empty()); + EXPECT_TRUE(value_view(container).empty()); + EXPECT_EQ(0u, key_view(container).size()); + EXPECT_EQ(0u, value_view(container).size()); + std::string s0 = "s0"; + std::string s1 = "s1"; + container.insert(std::make_pair("0", &s0)); + container.insert(std::make_pair("1", &s0)); + EXPECT_FALSE(key_view(container).empty()); + EXPECT_FALSE(value_view(container).empty()); + EXPECT_EQ(2u, key_view(container).size()); + EXPECT_EQ(2u, value_view(container).size()); + } +} + +TEST_F(IteratorAdaptorTest, View_IsEmpty) { + EXPECT_THAT(key_view(std::map()), IsEmpty()); + EXPECT_THAT(key_view(FixedSizeContainer<2, int, int>()), Not(IsEmpty())); +} + +TEST_F(IteratorAdaptorTest, View_SizeIs) { + EXPECT_THAT(key_view(std::map()), SizeIs(0)); + EXPECT_THAT(key_view(FixedSizeContainer<2, int, int>()), SizeIs(2)); +} + +TEST_F(IteratorAdaptorTest, View_Pointwise) { + typedef std::map MapType; + MapType my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + std::vector expected; + expected.push_back("a"); + expected.push_back("b"); + expected.push_back("c"); + + EXPECT_THAT(value_view(my_map), Pointwise(Eq(), expected)); +} + +TEST_F(IteratorAdaptorTest, DerefView) { + typedef std::vector ContainerType; + int v0 = 0; + int v1 = 1; + ContainerType c; + c.push_back(&v0); + c.push_back(&v1); + EXPECT_THAT(deref_view(c), ElementsAre(0, 1)); + *deref_view(c).begin() = 2; + EXPECT_THAT(v0, 2); + EXPECT_THAT(deref_view(c), ElementsAre(2, 1)); + const std::vector cc(c); + EXPECT_THAT(deref_view(cc), ElementsAre(2, 1)); +} + +TEST_F(IteratorAdaptorTest, ConstDerefView) { + typedef std::vector ContainerType; + const std::string s0 = "0"; + const std::string s1 = "1"; + ContainerType c; + c.push_back(&s0); + c.push_back(&s1); + EXPECT_THAT(deref_view(c), ElementsAre("0", "1")); +} + +TEST_F(IteratorAdaptorTest, DerefSecondView) { + typedef std::map ContainerType; + int v0 = 0; + int v1 = 1; + ContainerType c; + c.insert({10, &v0}); + c.insert({11, &v1}); + EXPECT_THAT(deref_second_view(c), ElementsAre(0, 1)); + *deref_second_view(c).begin() = 2; + EXPECT_THAT(v0, 2); + EXPECT_THAT(deref_second_view(c), ElementsAre(2, 1)); + const std::map cc(c); + EXPECT_THAT(deref_second_view(cc), ElementsAre(2, 1)); +} + +TEST_F(IteratorAdaptorTest, ConstDerefSecondView) { + typedef std::map ContainerType; + const std::string s0 = "0"; + const std::string s1 = "1"; + ContainerType c; + c.insert({10, &s0}); + c.insert({11, &s1}); + EXPECT_THAT(deref_second_view(c), ElementsAre("0", "1")); +} + +namespace { +template +std::vector ToVec(const T& t) { + return std::vector(t.begin(), t.end()); +} +} // namespace + +TEST_F(IteratorAdaptorTest, ReverseView) { + using firebase::firestore::util::reversed_view; + + int arr[] = {0, 1, 2, 3, 4, 5, 6}; + int* arr_end = arr + sizeof(arr) / sizeof(arr[0]); + std::vector vec(arr, arr_end); + const std::vector cvec(arr, arr_end); + + EXPECT_THAT(ToVec(reversed_view(vec)), ElementsAre(6, 5, 4, 3, 2, 1, 0)); + EXPECT_THAT(ToVec(reversed_view(cvec)), ElementsAre(6, 5, 4, 3, 2, 1, 0)); +} + +TEST_F(IteratorAdaptorTest, IteratorPtrConstConversions) { + // Users depend on this. It has to keep working. + std::vector v; + const std::vector& cv = v; + EXPECT_TRUE(make_iterator_ptr(cv.end()) == make_iterator_ptr(v.end())); + EXPECT_FALSE(make_iterator_ptr(cv.end()) != make_iterator_ptr(v.end())); + // EXPECT_TRUE(make_iterator_ptr(v.end()) == make_iterator_ptr(cv.end())); + // EXPECT_FALSE(make_iterator_ptr(v.end()) != make_iterator_ptr(cv.end())); +} + +TEST_F(IteratorAdaptorTest, IteratorPtrDeepConst) { + typedef std::vector PtrsToMutable; + typedef iterator_ptr ConstIter; + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE(IsConst::value); + + typedef iterator_ptr Iter; + EXPECT_TRUE((std::is_same::value)); + EXPECT_FALSE(IsConst::value); +} + +TEST_F(IteratorAdaptorTest, ReverseViewCxx11) { + using firebase::firestore::util::reversed_view; + + int arr[] = {0, 1, 2, 3, 4, 5, 6}; + int* arr_end = arr + sizeof(arr) / sizeof(arr[0]); + std::vector vec(arr, arr_end); + + // Try updates and demonstrate this work with C++11 for loops. + for (auto& i : reversed_view(vec)) ++i; + EXPECT_THAT(vec, ElementsAre(1, 2, 3, 4, 5, 6, 7)); +} + +TEST_F(IteratorAdaptorTest, BaseIterDanglingRefFirst) { + // Some iterators will hold 'on-board storage' for a synthesized value. + // We must take care not to pull our adapted reference from + // a temporary copy of the base iterator. See b/15113033. + typedef std::pair Val; + InlineStorageIter iter; + iterator_first> iter2(iter); + EXPECT_EQ(&iter2.base()->first, &*iter2); + EXPECT_EQ(&iter2.base()->first.d, &iter2->d); +} + +TEST_F(IteratorAdaptorTest, BaseIterDanglingRefSecond) { + typedef std::pair Val; + InlineStorageIter iter; + iterator_second> iter2(iter); + EXPECT_EQ(&iter2.base()->second, &*iter2); + EXPECT_EQ(&iter2.base()->second.d, &iter2->d); +} + +} // namespace From 650696e4a2b8091fb4784e7cb91c5573f2346147 Mon Sep 17 00:00:00 2001 From: Marek Gilbert Date: Fri, 26 Jan 2018 14:20:59 -0800 Subject: [PATCH 3/5] Strip dependency on absl_container from iterator_adapters_test --- .../firebase/firestore/util/CMakeLists.txt | 1 - .../firestore/util/iterator_adaptors_test.cc | 25 ++++++++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/Firestore/core/test/firebase/firestore/util/CMakeLists.txt b/Firestore/core/test/firebase/firestore/util/CMakeLists.txt index ed161163eb0..056d3143c8c 100644 --- a/Firestore/core/test/firebase/firestore/util/CMakeLists.txt +++ b/Firestore/core/test/firebase/firestore/util/CMakeLists.txt @@ -48,7 +48,6 @@ cc_test( string_printf_test.cc string_util_test.cc DEPENDS - absl_algorithm absl_base firebase_firestore_util gmock diff --git a/Firestore/core/test/firebase/firestore/util/iterator_adaptors_test.cc b/Firestore/core/test/firebase/firestore/util/iterator_adaptors_test.cc index 6740a23cfb1..782e76b3709 100644 --- a/Firestore/core/test/firebase/firestore/util/iterator_adaptors_test.cc +++ b/Firestore/core/test/firebase/firestore/util/iterator_adaptors_test.cc @@ -28,7 +28,6 @@ #include #include -#include "absl/algorithm/container.h" #include "absl/base/macros.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -599,7 +598,7 @@ TEST_F(IteratorAdaptorTest, DefaultAdaptorConstructorUsesDefaultValue) { ASSERT_TRUE(ptr_default == ptr_null); } -// Non C++11 test. We use c_copy() to avoid having to spell out the types. +// Non C++11 test. TEST_F(IteratorAdaptorTest, ValueView) { typedef unordered_map MapType; MapType my_map; @@ -609,7 +608,8 @@ TEST_F(IteratorAdaptorTest, ValueView) { const MapType c_map(my_map); std::set vals; - absl::c_copy(value_view(c_map), inserter(vals, vals.end())); + auto view = value_view(c_map); + std::copy(view.begin(), view.end(), inserter(vals, vals.end())); EXPECT_THAT(vals, ElementsAre("a", "b", "c")); } @@ -623,8 +623,8 @@ TEST_F(IteratorAdaptorTest, ValueView_Modify) { EXPECT_THAT(my_map, ElementsAre(Pair(0, 0), Pair(1, 1), Pair(2, 2))); value_view_type::type vv = value_view(my_map); - absl::c_replace(vv, 2, 3); - absl::c_replace(vv, 1, 2); + std::replace(vv.begin(), vv.end(), 2, 3); + std::replace(vv.begin(), vv.end(), 1, 2); EXPECT_THAT(my_map, ElementsAre(Pair(0, 0), Pair(1, 2), Pair(2, 3))); } @@ -668,8 +668,12 @@ TEST_F(IteratorAdaptorTest, ValueViewAndKeyViewCopy) { my_map[2] = "2"; std::set keys; std::set vals; - absl::c_copy(key_view(my_map), inserter(keys, keys.end())); - absl::c_copy(value_view(my_map), inserter(vals, vals.end())); + + auto kv = key_view(my_map); + std::copy(kv.begin(), kv.end(), inserter(keys, keys.end())); + + auto vv = value_view(my_map); + std::copy(vv.begin(), vv.end(), inserter(vals, vals.end())); EXPECT_THAT(keys, ElementsAre(0, 1, 2)); EXPECT_THAT(vals, ElementsAre("0", "1", "2")); } @@ -743,8 +747,11 @@ TEST_F(IteratorAdaptorTest, ValueViewWithPoorlyTypedHomeGrownContainer) { EXPECT_EQ(container.at(2), std::make_pair(2, std::string("2"))); std::vector keys; std::vector vals; - absl::c_copy(key_view(container), back_inserter(keys)); - absl::c_copy(value_view(container), back_inserter(vals)); + + auto kv = key_view(container); + std::copy(kv.begin(), kv.end(), back_inserter(keys)); + auto vv = value_view(container); + std::copy(vv.begin(), vv.end(), back_inserter(vals)); EXPECT_THAT(keys, ElementsAre(0, 1, 2)); EXPECT_THAT(vals, ElementsAre("0", "1", "2")); } From 02f4a4930ef73d1c3d5d000d841fc590c8dc5893 Mon Sep 17 00:00:00 2001 From: Marek Gilbert Date: Sat, 27 Jan 2018 12:30:59 -0800 Subject: [PATCH 4/5] Format and lint iterator_adaptors --- .../firestore/util/iterator_adaptors.h | 254 ++++++++++++------ .../firestore/util/iterator_adaptors_test.cc | 186 +++++++------ 2 files changed, 287 insertions(+), 153 deletions(-) diff --git a/Firestore/core/src/firebase/firestore/util/iterator_adaptors.h b/Firestore/core/src/firebase/firestore/util/iterator_adaptors.h index 22f45125db0..042fd720f23 100644 --- a/Firestore/core/src/firebase/firestore/util/iterator_adaptors.h +++ b/Firestore/core/src/firebase/firestore/util/iterator_adaptors.h @@ -92,18 +92,35 @@ class IteratorAdaptorBase { using reference = typename OutTraits::reference; using difference_type = typename OutTraits::difference_type; - IteratorAdaptorBase() : it_() {} - IteratorAdaptorBase(Iterator it) : it_(it) {} // NOLINT(runtime/explicit) + IteratorAdaptorBase() : it_() { + } + // NOLINTNEXTLINE(runtime/explicit) + IteratorAdaptorBase(Iterator it) : it_(it) { + } - Sub& sub() { return static_cast(*this); } - const Sub& sub() const { return static_cast(*this); } + Sub& sub() { + return static_cast(*this); + } + const Sub& sub() const { + return static_cast(*this); + } - const Iterator& base() const { return it_; } + const Iterator& base() const { + return it_; + } - reference get() const { return Extract(base()); } - reference operator*() const { return get(); } - pointer operator->() const { return &get(); } - reference operator[](difference_type d) const { return *(sub() + d); } + reference get() const { + return Extract(base()); + } + reference operator*() const { + return get(); + } + pointer operator->() const { + return &get(); + } + reference operator[](difference_type d) const { + return *(sub() + d); + } Sub& operator++() { ++it_; @@ -113,8 +130,12 @@ class IteratorAdaptorBase { --it_; return sub(); } - Sub operator++(int /*unused*/) { return it_++; } - Sub operator--(int /*unused*/) { return it_--; } + Sub operator++(int /*unused*/) { + return it_++; + } + Sub operator--(int /*unused*/) { + return it_--; + } Sub& operator+=(difference_type d) { it_ += d; @@ -125,22 +146,46 @@ class IteratorAdaptorBase { return sub(); } - bool operator==(Sub b) const { return base() == b.base(); } - bool operator!=(Sub b) const { return base() != b.base(); } + bool operator==(Sub b) const { + return base() == b.base(); + } + bool operator!=(Sub b) const { + return base() != b.base(); + } // These shouldn't be necessary, as implicit conversion from 'Iterator' // should be enough to make such comparisons work. - bool operator==(Iterator b) const { return *this == Sub(b); } - bool operator!=(Iterator b) const { return *this != Sub(b); } + bool operator==(Iterator b) const { + return *this == Sub(b); + } + bool operator!=(Iterator b) const { + return *this != Sub(b); + } - friend Sub operator+(Sub it, difference_type d) { return it.base() + d; } - friend Sub operator+(difference_type d, Sub it) { return it + d; } - friend Sub operator-(Sub it, difference_type d) { return it.base() - d; } - friend difference_type operator-(Sub a, Sub b) { return a.base() - b.base(); } + friend Sub operator+(Sub it, difference_type d) { + return it.base() + d; + } + friend Sub operator+(difference_type d, Sub it) { + return it + d; + } + friend Sub operator-(Sub it, difference_type d) { + return it.base() - d; + } + friend difference_type operator-(Sub a, Sub b) { + return a.base() - b.base(); + } - friend bool operator<(Sub a, Sub b) { return a.base() < b.base(); } - friend bool operator>(Sub a, Sub b) { return a.base() > b.base(); } - friend bool operator<=(Sub a, Sub b) { return a.base() <= b.base(); } - friend bool operator>=(Sub a, Sub b) { return a.base() >= b.base(); } + friend bool operator<(Sub a, Sub b) { + return a.base() < b.base(); + } + friend bool operator>(Sub a, Sub b) { + return a.base() > b.base(); + } + friend bool operator<=(Sub a, Sub b) { + return a.base() <= b.base(); + } + friend bool operator>=(Sub a, Sub b) { + return a.base() >= b.base(); + } private: Iterator it_; @@ -221,12 +266,15 @@ struct iterator_first internal::FirstPolicy> { using Base = internal::IteratorAdaptorBase, internal::FirstPolicy>; - iterator_first() {} + iterator_first() { + } iterator_first(It it) // NOLINT(runtime/explicit) - : Base(it) {} + : Base(it) { + } template iterator_first(iterator_first o) // NOLINT(runtime/explicit) - : Base(o.base()) {} + : Base(o.base()) { + } }; template @@ -252,12 +300,15 @@ struct iterator_second internal::SecondPolicy> { using Base = internal::IteratorAdaptorBase, internal::SecondPolicy>; - iterator_second() {} + iterator_second() { + } iterator_second(It it) // NOLINT(runtime/explicit) - : Base(it) {} + : Base(it) { + } template iterator_second(iterator_second o) // NOLINT(runtime/explicit) - : Base(o.base()) {} + : Base(o.base()) { + } }; template @@ -295,12 +346,15 @@ struct iterator_second_ptr internal::SecondPtrPolicy> { using Base = internal::IteratorAdaptorBase, internal::SecondPtrPolicy>; - iterator_second_ptr() {} + iterator_second_ptr() { + } iterator_second_ptr(It it) // NOLINT(runtime/explicit) - : Base(it) {} + : Base(it) { + } template iterator_second_ptr(iterator_second_ptr o) // NOLINT(runtime/explicit) - : Base(o.base()) {} + : Base(o.base()) { + } }; template @@ -337,12 +391,15 @@ struct iterator_ptr : internal::IteratorAdaptorBase, internal::PtrPolicy> { using Base = internal::IteratorAdaptorBase, internal::PtrPolicy>; - iterator_ptr() {} + iterator_ptr() { + } iterator_ptr(It it) // NOLINT(runtime/explicit) - : Base(it) {} + : Base(it) { + } template iterator_ptr(iterator_ptr o) // NOLINT(runtime/explicit) - : Base(o.base()) {} + : Base(o.base()) { + } }; template @@ -395,8 +452,12 @@ struct IterGenerator { static iterator end(container_type& c) { // NOLINT(runtime/references) return c.end(); } - static const_iterator begin(const container_type& c) { return c.begin(); } - static const_iterator end(const container_type& c) { return c.end(); } + static const_iterator begin(const container_type& c) { + return c.begin(); + } + static const_iterator end(const container_type& c) { + return c.end(); + } }; template @@ -424,7 +485,9 @@ struct ReversingIterGeneratorAdaptor { // Iter: the type of mutable iterator to generate // ConstIter: the type of constant iterator to generate // IterGenerator: a policy type that returns native iterators from a C -template > class iterator_view_helper { public: @@ -436,29 +499,47 @@ class iterator_view_helper { explicit iterator_view_helper( container_type& c) // NOLINT(runtime/references) - : c_(&c) {} + : c_(&c) { + } - iterator begin() { return iterator(IterGenerator::begin(container())); } - iterator end() { return iterator(IterGenerator::end(container())); } + iterator begin() { + return iterator(IterGenerator::begin(container())); + } + iterator end() { + return iterator(IterGenerator::end(container())); + } const_iterator begin() const { return const_iterator(IterGenerator::begin(container())); } const_iterator end() const { return const_iterator(IterGenerator::end(container())); } - const_iterator cbegin() const { return begin(); } - const_iterator cend() const { return end(); } - const container_type& container() const { return *c_; } - container_type& container() { return *c_; } + const_iterator cbegin() const { + return begin(); + } + const_iterator cend() const { + return end(); + } + const container_type& container() const { + return *c_; + } + container_type& container() { + return *c_; + } - bool empty() const { return begin() == end(); } - size_type size() const { return c_->size(); } + bool empty() const { + return begin() == end(); + } + size_type size() const { + return c_->size(); + } private: container_type* c_; }; -template > class const_iterator_view_helper { public: @@ -467,7 +548,8 @@ class const_iterator_view_helper { using value_type = typename std::iterator_traits::value_type; using size_type = typename internal::container_traits::size_type; - explicit const_iterator_view_helper(const container_type& c) : c_(&c) {} + explicit const_iterator_view_helper(const container_type& c) : c_(&c) { + } // Allow implicit conversion from the corresponding iterator_view_helper. // Erring on the side of constness should be allowed. E.g.: @@ -475,10 +557,12 @@ class const_iterator_view_helper { // key_view_type::type keys = key_view(m); // ok // key_view_type::type const_keys = key_view(m); // ok template - const_iterator_view_helper( - const iterator_view_helper& v) - : c_(&v.container()) {} + const_iterator_view_helper(const iterator_view_helper& v) + : c_(&v.container()) { + } const_iterator begin() const { return const_iterator(IterGenerator::begin(container())); @@ -486,12 +570,22 @@ class const_iterator_view_helper { const_iterator end() const { return const_iterator(IterGenerator::end(container())); } - const_iterator cbegin() const { return begin(); } - const_iterator cend() const { return end(); } - const container_type& container() const { return *c_; } + const_iterator cbegin() const { + return begin(); + } + const_iterator cend() const { + return end(); + } + const container_type& container() const { + return *c_; + } - bool empty() const { return begin() == end(); } - size_type size() const { return c_->size(); } + bool empty() const { + return begin() == end(); + } + size_type size() const { + return c_->size(); + } private: const container_type* c_; @@ -517,27 +611,30 @@ class const_iterator_view_helper { template struct key_view_type { using type = internal::iterator_view_helper< - C, iterator_first, + C, + iterator_first, iterator_first>; }; template struct key_view_type { - using type = internal::const_iterator_view_helper< - C, iterator_first>; + using type = internal:: + const_iterator_view_helper>; }; template struct value_view_type { - using type = internal::iterator_view_helper< - C, iterator_second, + using type = internal::iterator_view_helper< + C, + iterator_second, iterator_second>; }; template struct value_view_type { using type = internal::const_iterator_view_helper< - C, iterator_second>; + C, + iterator_second>; }; // The key_view and value_view functions provide pretty ways to iterate either @@ -599,14 +696,16 @@ typename value_view_type::type value_view(const C& map) { template struct deref_second_view_type { using type = internal::iterator_view_helper< - C, iterator_second_ptr, + C, + iterator_second_ptr, iterator_second_ptr>; }; template struct deref_second_view_type { using type = internal::const_iterator_view_helper< - C, iterator_second_ptr>; + C, + iterator_second_ptr>; }; template @@ -635,15 +734,16 @@ typename deref_second_view_type::type deref_second_view(const C& map) { template struct deref_view_type { - using type = internal::iterator_view_helper< - C, iterator_ptr, - iterator_ptr>; + using type = + internal::iterator_view_helper, + iterator_ptr>; }; template struct deref_view_type { - using type = internal::const_iterator_view_helper< - C, iterator_ptr>; + using type = internal:: + const_iterator_view_helper>; }; template @@ -677,9 +777,10 @@ struct reversed_view_type { internal::ReversingIterGeneratorAdaptor>; public: - using type = - internal::iterator_view_helper; + using type = internal::iterator_view_helper; }; template @@ -689,9 +790,8 @@ struct reversed_view_type { internal::ReversingIterGeneratorAdaptor>; public: - using type = - internal::const_iterator_view_helper; + using type = internal:: + const_iterator_view_helper; }; template diff --git a/Firestore/core/test/firebase/firestore/util/iterator_adaptors_test.cc b/Firestore/core/test/firebase/firestore/util/iterator_adaptors_test.cc index 782e76b3709..4cd44ccea32 100644 --- a/Firestore/core/test/firebase/firestore/util/iterator_adaptors_test.cc +++ b/Firestore/core/test/firebase/firestore/util/iterator_adaptors_test.cc @@ -35,37 +35,40 @@ using std::unordered_map; using std::unordered_set; -using testing::ElementsAre; -using testing::Eq; -using testing::IsEmpty; -using testing::Not; -using testing::Pair; -using testing::Pointwise; -using testing::SizeIs; -using firebase::firestore::util::deref_view; using firebase::firestore::util::deref_second_view; +using firebase::firestore::util::deref_view; using firebase::firestore::util::iterator_first; -using firebase::firestore::util::iterator_second; using firebase::firestore::util::iterator_ptr; +using firebase::firestore::util::iterator_second; using firebase::firestore::util::iterator_second_ptr; +using firebase::firestore::util::key_view; +using firebase::firestore::util::key_view_type; using firebase::firestore::util::make_iterator_first; -using firebase::firestore::util::make_iterator_second; using firebase::firestore::util::make_iterator_ptr; +using firebase::firestore::util::make_iterator_second; using firebase::firestore::util::make_iterator_second_ptr; -using firebase::firestore::util::key_view; -using firebase::firestore::util::key_view_type; using firebase::firestore::util::value_view; using firebase::firestore::util::value_view_type; +using testing::ElementsAre; +using testing::Eq; +using testing::IsEmpty; +using testing::Not; +using testing::Pair; +using testing::Pointwise; +using testing::SizeIs; namespace { -const char * kFirst[] = {"foo", "bar"}; -int kSecond[] = {1, 2}; +const char* kFirst[] = {"foo", "bar"}; +int kSecond[] = {1, 2}; const int kCount = ABSL_ARRAYSIZE(kFirst); -template struct IsConst : std::false_type {}; -template struct IsConst : std::true_type {}; -template struct IsConst : IsConst {}; +template +struct IsConst : std::false_type {}; +template +struct IsConst : std::true_type {}; +template +struct IsConst : IsConst {}; class IteratorAdaptorTest : public testing::Test { protected: @@ -81,18 +84,25 @@ class IteratorAdaptorTest : public testing::Test { template class InlineStorageIter : public std::iterator { public: - T* operator->() const { return get(); } - T& operator*() const { return *get(); } + T* operator->() const { + return get(); + } + T& operator*() const { + return *get(); + } private: - T* get() const { return &v_; } + T* get() const { + return &v_; + } mutable T v_; }; - struct X { int d; }; + struct X { + int d; + }; }; - TEST_F(IteratorAdaptorTest, HashMapFirst) { // Adapts an iterator to return the first value of a unordered_map::iterator. typedef unordered_map my_container; @@ -138,7 +148,7 @@ TEST_F(IteratorAdaptorTest, IteratorFirstConvertsToConst) { TEST_F(IteratorAdaptorTest, IteratorFirstConstEqNonConst) { // verify that const and non-const iterators return the same reference. - typedef std::vector > my_container; + typedef std::vector> my_container; typedef iterator_first my_iterator; typedef iterator_first my_const_iterator; my_container values; @@ -169,7 +179,7 @@ TEST_F(IteratorAdaptorTest, HashMapSecond) { values[kFirst[i]] = kSecond[i]; } for (iterator_second it = values.begin(); - it != values.end(); ++it) { + it != values.end(); ++it) { int v = *it; ASSERT_GT(v, 0); } @@ -192,7 +202,7 @@ TEST_F(IteratorAdaptorTest, IteratorSecondConvertsToConst) { TEST_F(IteratorAdaptorTest, IteratorSecondConstEqNonConst) { // verify that const and non-const iterators return the same reference. - typedef std::vector > my_container; + typedef std::vector> my_container; typedef iterator_second my_iterator; typedef iterator_second my_const_iterator; my_container values; @@ -255,7 +265,7 @@ TEST_F(IteratorAdaptorTest, IteratorPtrConst) { TEST_F(IteratorAdaptorTest, IteratorSecondPtrConstEqNonConst) { // verify that const and non-const iterators return the same reference. - typedef std::vector > my_container; + typedef std::vector> my_container; typedef iterator_second_ptr my_iterator; typedef iterator_second_ptr my_const_iterator; my_container values; @@ -321,26 +331,25 @@ TEST_F(IteratorAdaptorTest, ListSecondConst) { int i = 0; const my_list* cvalues = &values; for (iterator_second it = cvalues->begin(); - it != cvalues->end(); ++it) { + it != cvalues->end(); ++it) { ASSERT_EQ(*it, kSecond[i++]); } } TEST_F(IteratorAdaptorTest, VectorSecond) { // Adapts an iterator to return the second value of a vector::iterator. - std::vector > values; + std::vector> values; for (int i = 0; i < kCount; ++i) { values.push_back(std::pair(kFirst[i], kSecond[i])); } int i = 0; - for (iterator_second< std::vector >::iterator > - it = values.begin(); + for (iterator_second>::iterator> it = + values.begin(); it != values.end(); ++it) { ASSERT_EQ(*it, kSecond[i++]); } } - // Tests iterator_second_ptr with a map where values are regular pointers. TEST_F(IteratorAdaptorTest, HashMapSecondPtr) { typedef unordered_map my_container; @@ -363,7 +372,7 @@ TEST_F(IteratorAdaptorTest, HashMapSecondPtr) { // Tests iterator_second_ptr with a map where values are wrapped into // linked_ptr. TEST_F(IteratorAdaptorTest, HashMapSecondPtrLinkedPtr) { - typedef unordered_map > my_container; + typedef unordered_map> my_container; typedef iterator_second_ptr my_iterator; my_container values; for (int i = 0; i < kCount; ++i) { @@ -417,8 +426,8 @@ TEST_F(IteratorAdaptorTest, IteratorPtrtConstEqNonConst) { my_const_iterator c_iter1 = iter1; const my_const_iterator c_iter2 = iter1; for (int i = 0; i < kCount; ++i) { - int &v1 = iter1[i]; - int &v2 = iter2[i]; + int& v1 = iter1[i]; + int& v2 = iter2[i]; EXPECT_EQ(&v1, kSecond + i); EXPECT_EQ(&v1, &v2); const int& cv1 = c_iter1[i]; @@ -428,9 +437,10 @@ TEST_F(IteratorAdaptorTest, IteratorPtrtConstEqNonConst) { } } -// Tests iterator_ptr with a vector where values are wrapped into std::shared_ptr. +// Tests iterator_ptr with a vector where values are wrapped into +// std::shared_ptr. TEST_F(IteratorAdaptorTest, IteratorPtrLinkedPtr) { - typedef std::vector > my_container; + typedef std::vector> my_container; typedef iterator_ptr my_iterator; my_container values; for (int i = 0; i < kCount; ++i) { @@ -455,7 +465,7 @@ TEST_F(IteratorAdaptorTest, IteratorPtrConvertsToConst) { } TEST_F(IteratorAdaptorTest, IteratorFirstHasRandomAccessMethods) { - typedef std::vector > my_container; + typedef std::vector> my_container; typedef iterator_first my_iterator; my_container values; @@ -480,7 +490,7 @@ TEST_F(IteratorAdaptorTest, IteratorFirstHasRandomAccessMethods) { } TEST_F(IteratorAdaptorTest, IteratorSecondHasRandomAccessMethods) { - typedef std::vector > my_container; + typedef std::vector> my_container; typedef iterator_second my_iterator; my_container values; @@ -505,7 +515,7 @@ TEST_F(IteratorAdaptorTest, IteratorSecondHasRandomAccessMethods) { } TEST_F(IteratorAdaptorTest, IteratorSecondPtrHasRandomAccessMethods) { - typedef std::vector > my_container; + typedef std::vector> my_container; typedef iterator_second_ptr my_iterator; ASSERT_GE(kCount, 2); @@ -557,15 +567,21 @@ TEST_F(IteratorAdaptorTest, IteratorPtrHasRandomAccessMethods) { EXPECT_EQ(88, value2); } -class MyInputIterator : - public std::iterator { +class MyInputIterator + : public std::iterator { public: - explicit MyInputIterator(int* x) : x_(x) {} - const int* operator*() const { return x_; } - MyInputIterator& operator++() { ++*x_; return *this; } + explicit MyInputIterator(int* x) : x_(x) { + } + const int* operator*() const { + return x_; + } + MyInputIterator& operator++() { + ++*x_; + return *this; + } private: - int *x_; + int* x_; }; TEST_F(IteratorAdaptorTest, IteratorPtrCanWrapInputIterator) { @@ -573,9 +589,12 @@ TEST_F(IteratorAdaptorTest, IteratorPtrCanWrapInputIterator) { MyInputIterator it(&x); iterator_ptr it1(it); - EXPECT_EQ(0, *it1); ++it1; - EXPECT_EQ(1, *it1); ++it1; - EXPECT_EQ(2, *it1); ++it1; + EXPECT_EQ(0, *it1); + ++it1; + EXPECT_EQ(1, *it1); + ++it1; + EXPECT_EQ(2, *it1); + ++it1; } // Tests that a default-constructed adaptor is equal to an adaptor explicitly @@ -644,15 +663,14 @@ TEST_F(IteratorAdaptorTest, ValueViewOfValueView) { static_assert( (std::is_same::value), + value_view_map_int_pair_int_str_type::value_type>::value), "value_view_value_type_"); typedef value_view_type::type view_view_type; - static_assert( - (std::is_same::value), - "view_view_type_"); + static_assert((std::is_same::value), + "view_view_type_"); value_view_map_int_pair_int_str_type vv = value_view(my_map); view_view_type helper = value_view(vv); @@ -695,7 +713,7 @@ TEST_F(IteratorAdaptorTest, ValueViewAndKeyViewRangeBasedLoop) { EXPECT_THAT(vals, ElementsAre("0", "1", "2")); } -template +template class FixedSizeContainer { public: // NOTE: the container does on purpose not define: @@ -705,17 +723,33 @@ class FixedSizeContainer { typedef value_type* iterator; typedef const value_type* const_iterator; - FixedSizeContainer() {} - const_iterator begin() const { return &values[0]; } - iterator begin() { return &values[0]; } - const_iterator end() const { return &values[N]; } - iterator end() { return &values[N]; } - value_type at(int n) const { return values[n]; } - value_type& operator[](int n) { return values[n]; } - int size() const { return N; } + FixedSizeContainer() { + } + const_iterator begin() const { + return &values[0]; + } + iterator begin() { + return &values[0]; + } + const_iterator end() const { + return &values[N]; + } + iterator end() { + return &values[N]; + } + value_type at(int n) const { + return values[n]; + } + value_type& operator[](int n) { + return values[n]; + } + int size() const { + return N; + } private: - value_type values[N ? N : 1]; + static constexpr int kAllocatedSize = N ? N : 1; + value_type values[kAllocatedSize]; // NOTE: the container does on purpose not define: // reference, const_reference, pointer, const_pointer, size_type, // difference_type, empty(). @@ -787,8 +821,8 @@ TEST_F(IteratorAdaptorTest, ValueViewInConstContext) { const iterator_view_helper< unordered_map, iterator_second::iterator>, - iterator_second::const_iterator> > const_view = - value_view(my_map); + iterator_second::const_iterator>> + const_view = value_view(my_map); for (iterator_second::const_iterator> it = const_view.begin(); it != const_view.end(); ++it) { @@ -851,8 +885,8 @@ TEST_F(IteratorAdaptorTest, ConstValueViewInConstContext) { const unordered_map& const_map = my_map; std::set vals; - const value_view_type >::type const_view = - value_view(const_map); + const value_view_type>::type + const_view = value_view(const_map); for (iterator_second::const_iterator> it = const_view.begin(); it != const_view.end(); ++it) { @@ -909,7 +943,7 @@ TEST_F(IteratorAdaptorTest, KeyViewInConstContext) { my_map[2] = "c"; std::set vals; - const key_view_type >::type const_view = + const key_view_type>::type const_view = key_view(my_map); for (iterator_first::const_iterator> it = const_view.begin(); @@ -973,7 +1007,7 @@ TEST_F(IteratorAdaptorTest, ConstKeyViewInConstContext) { const unordered_map& const_map = my_map; std::set vals; - const key_view_type >::type const_view = + const key_view_type>::type const_view = key_view(const_map); for (iterator_first::const_iterator> it = const_view.begin(); @@ -993,9 +1027,9 @@ TEST_F(IteratorAdaptorTest, IteratorViewHelperDefinesIterator) { my_set.insert(0); my_set.insert(2); - typedef iterator_view_helper, - unordered_set::iterator, - unordered_set::const_iterator> SetView; + typedef iterator_view_helper, unordered_set::iterator, + unordered_set::const_iterator> + SetView; SetView set_view(my_set); unordered_set vals; for (SetView::iterator it = set_view.begin(); it != set_view.end(); ++it) { @@ -1014,13 +1048,13 @@ TEST_F(IteratorAdaptorTest, IteratorViewHelperDefinesConstIterator) { my_set.insert(0); my_set.insert(2); - typedef iterator_view_helper, - unordered_set::iterator, - unordered_set::const_iterator> SetView; + typedef iterator_view_helper, unordered_set::iterator, + unordered_set::const_iterator> + SetView; SetView set_view(my_set); unordered_set vals; - for (SetView::const_iterator it = set_view.begin(); - it != set_view.end(); ++it) { + for (SetView::const_iterator it = set_view.begin(); it != set_view.end(); + ++it) { vals.insert(*it); } From 13e178744e53327ce4283f9bde985e301147a214 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Mon, 29 Jan 2018 06:51:14 -0800 Subject: [PATCH 5/5] More flexible copyright checking --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9d505f85ef0..5ee84f2a755 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,7 @@ before_install: script: - "! git grep -I ' $'" # Fail on trailing whitespace in non-binary files - - "! git grep -L --name-only 'Copyright [0-9]* Google' | grep -v third_party | egrep '\\.(m|h|cc|mm|c)$'" + - "! git grep -EL --name-only 'Copyright [0-9]{4}.*Google' | grep -v third_party | egrep '\\.(m|h|cc|mm|c)$'" - ./scripts/style.sh test-only # Validate clang-format compliance - | if [ $SKIP_FIREBASE != 1 ]; then