Factor out `ArrowProxy`, a common implementation of `iterator::pointer` when
`iterator::reference` is not a true reference.
Move `ReferencePair` from `csv_record.h` to `iterable.h`. It is not specific for
`CsvRecord` but applicable to any map type with separate storage for keys and
values.
Minor changes to `{Linear,Chunked}SortedStringSet` iterators to satisfy C++20
requirements:
* Make `ChunkedSortedStringSet` an alias to an internal `IteratorImpl`
instantiation instead of deriving from it. This makes return types correct.
* Let
`{Linear,Chunked}SortedStringSet::NextInsertIterator::reference::operator=`
be const (this is shallow), as required by `std::indirectly_writable`.
PiperOrigin-RevId: 883153178
diff --git a/riegeli/base/BUILD b/riegeli/base/BUILD
index 748ee98..82cc3f9 100644
--- a/riegeli/base/BUILD
+++ b/riegeli/base/BUILD
@@ -49,6 +49,7 @@
hdrs = ["iterable.h"],
deps = [
":type_traits",
+ "@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/base:nullability",
],
)
@@ -553,6 +554,7 @@
":cord_utils",
":external_data",
":initializer",
+ ":iterable",
":memory_estimator",
":new_aligned",
":null_safe_memcpy",
@@ -595,6 +597,7 @@
":external_data",
":external_ref",
":global",
+ ":iterable",
":shared_buffer",
":types",
"@com_google_absl//absl/base:core_headers",
@@ -643,6 +646,7 @@
":bytes_ref",
":compact_string",
":compare",
+ ":iterable",
":type_traits",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/strings:string_view",
diff --git a/riegeli/base/byte_fill.h b/riegeli/base/byte_fill.h
index 9b35ba5..f003b65 100644
--- a/riegeli/base/byte_fill.h
+++ b/riegeli/base/byte_fill.h
@@ -34,6 +34,7 @@
#include "riegeli/base/chain.h"
#include "riegeli/base/compare.h"
#include "riegeli/base/external_data.h"
+#include "riegeli/base/iterable.h"
#include "riegeli/base/shared_buffer.h"
#include "riegeli/base/types.h"
@@ -290,18 +291,9 @@
using iterator_category = std::input_iterator_tag;
using value_type = BlockRef;
using reference = value_type;
+ using pointer = ArrowProxy<reference>;
using difference_type = ptrdiff_t;
- class pointer {
- public:
- const reference* operator->() const { return &ref_; }
-
- private:
- friend class BlockIterator;
- explicit pointer(reference ref) : ref_(ref) {}
- reference ref_;
- };
-
BlockIterator() = default;
BlockIterator(const BlockIterator& that) = default;
diff --git a/riegeli/base/chain_details.h b/riegeli/base/chain_details.h
index 2dcc40d..d48c477 100644
--- a/riegeli/base/chain_details.h
+++ b/riegeli/base/chain_details.h
@@ -45,6 +45,7 @@
#include "riegeli/base/external_ref_support.h"
#include "riegeli/base/initializer.h"
#include "riegeli/base/intrusive_shared_ptr.h"
+#include "riegeli/base/iterable.h"
#include "riegeli/base/memory_estimator.h"
#include "riegeli/base/new_aligned.h"
#include "riegeli/base/ownership.h"
@@ -182,18 +183,9 @@
using iterator_category = std::input_iterator_tag;
using value_type = BlockRef;
using reference = value_type;
+ using pointer = ArrowProxy<reference>;
using difference_type = ptrdiff_t;
- class pointer {
- public:
- const reference* operator->() const { return &ref_; }
-
- private:
- friend class BlockIterator;
- explicit pointer(reference ref) : ref_(ref) {}
- reference ref_;
- };
-
BlockIterator() = default;
explicit BlockIterator(const Chain* chain ABSL_ATTRIBUTE_LIFETIME_BOUND,
diff --git a/riegeli/base/iterable.h b/riegeli/base/iterable.h
index bac149c..e73ddcc 100644
--- a/riegeli/base/iterable.h
+++ b/riegeli/base/iterable.h
@@ -21,6 +21,7 @@
#include <type_traits>
#include <utility>
+#include "absl/base/attributes.h"
#include "absl/base/nullability.h"
#include "riegeli/base/type_traits.h"
@@ -259,6 +260,92 @@
// `size(iterable)` after `using std::size;`.
using iterable_internal::IterableHasSize;
+// Represents the result of `operator->` if `operator*` returns a proxy
+// object rather than a true reference. In particular this can be used as
+// `iterator::pointer` if `iterator::reference` is not a true reference.
+template <typename Reference>
+class ArrowProxy {
+ public:
+ explicit ArrowProxy(Reference ref) : ref_(std::move(ref)) {}
+
+ ArrowProxy(const ArrowProxy& that) = default;
+ ArrowProxy& operator=(const ArrowProxy& that) = default;
+
+ ArrowProxy(ArrowProxy&& that) noexcept = default;
+ ArrowProxy& operator=(ArrowProxy&& that) noexcept = default;
+
+ const Reference* operator->() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+ return &ref_;
+ }
+
+ private:
+ Reference ref_;
+};
+
+// A pair-like type to be used as `iterator::reference` for iterators over a map
+// with a separate storage for keys and values. In C++20 this lets the iterator
+// satisfy `std::indirectly_readable`.
+//
+// It extends `std::pair<T1, T2>` with conversions from `std::pair<U1, U2>&`
+// and with `std::basic_common_reference` specializations.
+//
+// Since C++23, `std::pair<T1, T2>` can be used directly instead.
+template <typename T1, typename T2>
+class ReferencePair : public std::pair<T1, T2> {
+ public:
+ using ReferencePair::pair::pair;
+
+ template <
+ class U1, class U2,
+ std::enable_if_t<
+ std::conjunction_v<
+ std::is_constructible<T1, U1&>, std::is_constructible<T2, U2&>,
+ std::negation<std::conjunction<std::is_convertible<U1&, T1>,
+ std::is_convertible<U2&, T2>>>>,
+ int> = 0>
+ explicit constexpr ReferencePair(std::pair<U1, U2>& p)
+ : ReferencePair::pair(p.first, p.second) {}
+
+ template <class U1, class U2,
+ std::enable_if_t<std::conjunction_v<std::is_convertible<U1&, T1>,
+ std::is_convertible<U2&, T2>>,
+ int> = 0>
+ /*implicit*/ constexpr ReferencePair(std::pair<U1, U2>& p)
+ : ReferencePair::pair(p.first, p.second) {}
+};
+
} // namespace riegeli
+#if __cplusplus >= 202002L
+
+template <typename T1, typename T2, typename U1, typename U2,
+ template <typename> class TQual, template <typename> class UQual>
+struct std::basic_common_reference<riegeli::ReferencePair<T1, T2>,
+ std::pair<U1, U2>, TQual, UQual> {
+ using type =
+ riegeli::ReferencePair<std::common_reference_t<TQual<T1>, UQual<U1>>,
+ std::common_reference_t<TQual<T2>, UQual<U2>>>;
+};
+
+template <typename T1, typename T2, typename U1, typename U2,
+ template <typename> class TQual, template <typename> class UQual>
+struct std::basic_common_reference<
+ std::pair<T1, T2>, riegeli::ReferencePair<U1, U2>, TQual, UQual> {
+ using type =
+ riegeli::ReferencePair<std::common_reference_t<TQual<T1>, UQual<U1>>,
+ std::common_reference_t<TQual<T2>, UQual<U2>>>;
+};
+
+template <typename T1, typename T2, typename U1, typename U2,
+ template <typename> class TQual, template <typename> class UQual>
+struct std::basic_common_reference<riegeli::ReferencePair<T1, T2>,
+ riegeli::ReferencePair<U1, U2>, TQual,
+ UQual> {
+ using type =
+ riegeli::ReferencePair<std::common_reference_t<TQual<T1>, UQual<U1>>,
+ std::common_reference_t<TQual<T2>, UQual<U2>>>;
+};
+
+#endif
+
#endif // RIEGELI_BASE_ITERABLE_H_
diff --git a/riegeli/base/optional_compact_string.h b/riegeli/base/optional_compact_string.h
index bc67524..a6bf69e 100644
--- a/riegeli/base/optional_compact_string.h
+++ b/riegeli/base/optional_compact_string.h
@@ -27,6 +27,7 @@
#include "riegeli/base/bytes_ref.h"
#include "riegeli/base/compact_string.h"
#include "riegeli/base/compare.h"
+#include "riegeli/base/iterable.h"
#include "riegeli/base/type_traits.h"
namespace riegeli {
@@ -39,17 +40,6 @@
// `const char*`, but not as `CompactString`, except by copying or moving from.
class ABSL_ATTRIBUTE_TRIVIAL_ABI OptionalCompactString
: public WithCompare<OptionalCompactString> {
- private:
- class StringViewPointer {
- public:
- const absl::string_view* operator->() const { return &ref_; }
-
- private:
- friend class OptionalCompactString;
- explicit StringViewPointer(absl::string_view ref) : ref_(ref) {}
- absl::string_view ref_;
- };
-
public:
// Creates a null `OptionalCompactString`.
OptionalCompactString() = default;
@@ -137,11 +127,12 @@
return CompactString::ViewFromRaw(&repr_);
}
- StringViewPointer operator->() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+ ArrowProxy<absl::string_view> operator->() const
+ ABSL_ATTRIBUTE_LIFETIME_BOUND {
RIEGELI_ASSERT(*this != nullptr)
<< "Failed precondition of OptionalCompactString::operator->: "
"OptionalCompactString is nullptr";
- return StringViewPointer(**this);
+ return ArrowProxy<absl::string_view>(**this);
}
// Ensures that the value is NUL-terminated after its size and returns
diff --git a/riegeli/containers/chunked_sorted_string_set.h b/riegeli/containers/chunked_sorted_string_set.h
index 1a03d9a..0ab02c4 100644
--- a/riegeli/containers/chunked_sorted_string_set.h
+++ b/riegeli/containers/chunked_sorted_string_set.h
@@ -94,9 +94,9 @@
size_t size_hint_ = 0;
};
- class Iterator;
using SplitElement = LinearSortedStringSet::SplitElement;
- class SplitElementIterator;
+ using SplitElementIterator =
+ IteratorImpl<LinearSortedStringSet::SplitElementIterator>;
class SplitElements;
class Builder;
class NextInsertIterator;
@@ -169,7 +169,7 @@
using value_type = absl::string_view;
using reference = value_type;
using const_reference = reference;
- using iterator = Iterator;
+ using iterator = IteratorImpl<LinearSortedStringSet::iterator>;
using const_iterator = iterator;
using size_type = size_t;
using difference_type = ptrdiff_t;
@@ -227,10 +227,10 @@
default;
// Iteration over the set.
- Iterator begin() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
- Iterator cbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
- Iterator end() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
- Iterator cend() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
+ iterator begin() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
+ iterator cbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
+ iterator end() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
+ iterator cend() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
// Returns a proxy for `LinearSortedStringSet` where each element is
// represented as `SplitElement` rather than `absl::string_view`. This is
@@ -334,7 +334,8 @@
// Iterates over a `LinearSortedStringSet` in the sorted order.
template <typename LinearIterator>
-class ChunkedSortedStringSet::IteratorImpl {
+class ChunkedSortedStringSet::IteratorImpl
+ : public WithEqual<IteratorImpl<LinearIterator>> {
public:
// `iterator_concept` is only `std::input_iterator_tag` because the
// `std::forward_iterator` requirement and above require references to remain
@@ -346,18 +347,9 @@
using iterator_category = std::input_iterator_tag;
using value_type = typename LinearIterator::value_type;
using reference = value_type;
+ using pointer = ArrowProxy<reference>;
using difference_type = ptrdiff_t;
- class pointer {
- public:
- const reference* operator->() const { return &ref_; }
-
- private:
- friend class IteratorImpl<LinearIterator>;
- explicit pointer(const reference& ref) : ref_(ref) {}
- const reference& ref_;
- };
-
// A sentinel value, equal to `end()`.
IteratorImpl() = default;
@@ -395,8 +387,10 @@
return tmp;
}
- protected:
- static bool Equal(const IteratorImpl& a, const IteratorImpl& b) {
+ // Iterators can be compared even if they are associated with different
+ // `ChunkedSortedStringSet` objects. All `end()` values are equal, while all
+ // other values are not equal.
+ friend bool operator==(const IteratorImpl& a, const IteratorImpl& b) {
return a.current_iterator_ == b.current_iterator_;
}
@@ -421,35 +415,6 @@
const ChunkedSortedStringSet* set_ = nullptr;
};
-class ChunkedSortedStringSet::Iterator
- : public IteratorImpl<LinearSortedStringSet::Iterator>,
- public WithEqual<Iterator> {
- public:
- using Iterator::IteratorImpl::IteratorImpl;
-
- // Iterators can be compared even if they are associated with different
- // `ChunkedSortedStringSet` objects. All `end()` values are equal, while all
- // other values are not equal.
- friend bool operator==(const Iterator& a, const Iterator& b) {
- return Equal(a, b);
- }
-};
-
-class ChunkedSortedStringSet::SplitElementIterator
- : public IteratorImpl<LinearSortedStringSet::SplitElementIterator>,
- public WithEqual<SplitElementIterator> {
- public:
- using SplitElementIterator::IteratorImpl::IteratorImpl;
-
- // Iterators can be compared even if they are associated with different
- // `ChunkedSortedStringSet` objects. All `end()` values are equal, while all
- // other values are not equal.
- friend bool operator==(const SplitElementIterator& a,
- const SplitElementIterator& b) {
- return Equal(a, b);
- }
-};
-
// A proxy for `ChunkedSortedStringSet` where each element is represented as
// `SplitElement` rather than `absl::string_view`. This is more efficient but
// less convenient.
@@ -587,8 +552,8 @@
using iterator_concept = std::output_iterator_tag;
using iterator_category = std::output_iterator_tag;
using value_type = absl::string_view;
- using difference_type = ptrdiff_t;
using pointer = void;
+ using difference_type = ptrdiff_t;
class reference {
public:
@@ -597,13 +562,13 @@
// `std::string&&` is accepted with a template to avoid implicit conversions
// to `std::string` which can be ambiguous against `absl::string_view`
// (e.g. `const char*`).
- reference& operator=(absl::string_view element) {
+ const reference& operator=(absl::string_view element) const {
builder_->InsertNext(element);
return *this;
}
template <typename Element,
std::enable_if_t<std::is_same_v<Element, std::string>, int> = 0>
- reference& operator=(Element&& element) {
+ const reference& operator=(Element&& element) const {
// `std::move(element)` is correct and `std::forward<Element>(element)` is
// not necessary: `Element` is always `std::string`, never an lvalue
// reference.
@@ -691,22 +656,22 @@
return builder.Build();
}
-inline ChunkedSortedStringSet::Iterator ChunkedSortedStringSet::begin() const
+inline ChunkedSortedStringSet::iterator ChunkedSortedStringSet::begin() const
ABSL_ATTRIBUTE_LIFETIME_BOUND {
- return Iterator(this);
+ return iterator(this);
}
-inline ChunkedSortedStringSet::Iterator ChunkedSortedStringSet::cbegin() const
+inline ChunkedSortedStringSet::iterator ChunkedSortedStringSet::cbegin() const
ABSL_ATTRIBUTE_LIFETIME_BOUND {
return begin();
}
-inline ChunkedSortedStringSet::Iterator ChunkedSortedStringSet::end() const
+inline ChunkedSortedStringSet::iterator ChunkedSortedStringSet::end() const
ABSL_ATTRIBUTE_LIFETIME_BOUND {
- return Iterator();
+ return iterator();
}
-inline ChunkedSortedStringSet::Iterator ChunkedSortedStringSet::cend() const
+inline ChunkedSortedStringSet::iterator ChunkedSortedStringSet::cend() const
ABSL_ATTRIBUTE_LIFETIME_BOUND {
return end();
}
diff --git a/riegeli/containers/linear_sorted_string_set.h b/riegeli/containers/linear_sorted_string_set.h
index aa563a9..6baa660 100644
--- a/riegeli/containers/linear_sorted_string_set.h
+++ b/riegeli/containers/linear_sorted_string_set.h
@@ -296,18 +296,9 @@
using iterator_category = std::input_iterator_tag;
using value_type = absl::string_view;
using reference = value_type;
+ using pointer = ArrowProxy<reference>;
using difference_type = ptrdiff_t;
- class pointer {
- public:
- const reference* operator->() const { return &ref_; }
-
- private:
- friend class Iterator;
- explicit pointer(const reference& ref) : ref_(ref) {}
- const reference& ref_;
- };
-
// A sentinel value, equal to `end()`.
Iterator() = default;
@@ -481,18 +472,9 @@
using iterator_category = std::input_iterator_tag;
using value_type = SplitElement;
using reference = value_type;
+ using pointer = ArrowProxy<reference>;
using difference_type = ptrdiff_t;
- class pointer {
- public:
- const reference* operator->() const { return &ref_; }
-
- private:
- friend class SplitElementIterator;
- explicit pointer(const reference& ref) : ref_(ref) {}
- const reference& ref_;
- };
-
// A sentinel value, equal to `end()`.
SplitElementIterator() = default;
@@ -708,13 +690,13 @@
// `std::string&&` is accepted with a template to avoid implicit conversions
// to `std::string` which can be ambiguous against `absl::string_view`
// (e.g. `const char*`).
- reference& operator=(absl::string_view element) {
+ const reference& operator=(absl::string_view element) const {
builder_->InsertNext(element);
return *this;
}
template <typename Element,
std::enable_if_t<std::is_same_v<Element, std::string>, int> = 0>
- reference& operator=(Element&& element) {
+ const reference& operator=(Element&& element) const {
// `std::move(element)` is correct and `std::forward<Element>(element)` is
// not necessary: `Element` is always `std::string`, never an lvalue
// reference.
diff --git a/riegeli/csv/csv_record.h b/riegeli/csv/csv_record.h
index 058fa67..b36db4c 100644
--- a/riegeli/csv/csv_record.h
+++ b/riegeli/csv/csv_record.h
@@ -51,36 +51,6 @@
namespace riegeli::csv_internal {
-// A pair-like type which supports C++20 `std::common_reference` with similar
-// enough pairs. This is needed for `CsvRecord::{,const_}iterator` to satisfy
-// C++20 input iterator requirements.
-//
-// Since C++23, `std::pair<T1, T2>` can be used directly instead, because it has
-// the necessary conversions and `std::basic_common_reference` specializations.
-template <typename T1, typename T2>
-class ReferencePair : public std::pair<T1, T2> {
- public:
- using ReferencePair::pair::pair;
-
- template <
- class U1, class U2,
- std::enable_if_t<
- std::conjunction_v<
- std::is_constructible<T1, U1&>, std::is_constructible<T2, U2&>,
- std::negation<std::conjunction<std::is_convertible<U1&, T1>,
- std::is_convertible<U2&, T2>>>>,
- int> = 0>
- explicit constexpr ReferencePair(std::pair<U1, U2>& p)
- : ReferencePair::pair(p.first, p.second) {}
-
- template <class U1, class U2,
- std::enable_if_t<std::conjunction_v<std::is_convertible<U1&, T1>,
- std::is_convertible<U2&, T2>>,
- int> = 0>
- /*implicit*/ constexpr ReferencePair(std::pair<U1, U2>& p)
- : ReferencePair::pair(p.first, p.second) {}
-};
-
// `ToStringVector()` converts an iterable of elements convertible to
// `absl::string_view` to a `std::vector<std::string>`.
@@ -96,40 +66,6 @@
} // namespace riegeli::csv_internal
-#if __cplusplus >= 202002L
-
-template <typename T1, typename T2, template <typename> class TQual,
- template <typename> class UQual>
-struct std::basic_common_reference<riegeli::csv_internal::ReferencePair<T1, T2>,
- std::pair<std::string, std::string>, TQual,
- UQual> {
- using type = riegeli::csv_internal::ReferencePair<
- std::common_reference_t<TQual<T1>, UQual<std::string>>,
- std::common_reference_t<TQual<T2>, UQual<std::string>>>;
-};
-
-template <typename T1, typename T2, template <typename> class TQual,
- template <typename> class UQual>
-struct std::basic_common_reference<std::pair<std::string, std::string>,
- riegeli::csv_internal::ReferencePair<T1, T2>,
- TQual, UQual> {
- using type = riegeli::csv_internal::ReferencePair<
- std::common_reference_t<TQual<std::string>, UQual<T1>>,
- std::common_reference_t<TQual<std::string>, UQual<T2>>>;
-};
-
-template <typename T1, typename T2, typename U1, typename U2,
- template <typename> class TQual, template <typename> class UQual>
-struct std::basic_common_reference<riegeli::csv_internal::ReferencePair<T1, T2>,
- riegeli::csv_internal::ReferencePair<U1, U2>,
- TQual, UQual> {
- using type = riegeli::csv_internal::ReferencePair<
- std::common_reference_t<TQual<T1>, UQual<U1>>,
- std::common_reference_t<TQual<T2>, UQual<U2>>>;
-};
-
-#endif
-
namespace riegeli {
// A normalizer for `CsvHeader` and `CsvReaderBase::Options::set_normalizer()`,
@@ -622,22 +558,13 @@
// `LegacyForwardIterator` requirement and above require `reference` to be
// a true reference type.
using iterator_category = std::input_iterator_tag;
- using value_type = std::pair<std::string, std::string>;
- using reference = csv_internal::ReferencePair<
- const std::string&,
- typename std::iterator_traits<FieldIterator>::reference>;
+ using value_type = std::pair<const std::string, std::string>;
+ using reference =
+ ReferencePair<const std::string&,
+ typename std::iterator_traits<FieldIterator>::reference>;
+ using pointer = ArrowProxy<reference>;
using difference_type = ptrdiff_t;
- class pointer {
- public:
- const reference* operator->() const { return &ref_; }
-
- private:
- friend class IteratorImpl<FieldIterator>;
- explicit pointer(reference ref) : ref_(ref) {}
- reference ref_;
- };
-
IteratorImpl() = default;
// Conversion from `iterator` to `const_iterator`.
@@ -695,11 +622,9 @@
public:
using key_type = std::string;
using mapped_type = std::string;
- using value_type = std::pair<std::string, std::string>;
- using reference =
- csv_internal::ReferencePair<const std::string&, std::string&>;
- using const_reference =
- csv_internal::ReferencePair<const std::string&, const std::string&>;
+ using value_type = std::pair<const std::string, std::string>;
+ using reference = ReferencePair<const std::string&, std::string&>;
+ using const_reference = ReferencePair<const std::string&, const std::string&>;
using iterator = IteratorImpl<std::vector<std::string>::iterator>;
using const_iterator = IteratorImpl<std::vector<std::string>::const_iterator>;
using reverse_iterator = std::reverse_iterator<iterator>;