make iterator_adaptor easier to use (#364)

This commit is contained in:
Hans Dembinski 2022-09-17 18:35:33 +02:00 committed by GitHub
parent 4699dbc0e8
commit 7cf67610f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 114 additions and 75 deletions

View File

@ -34,4 +34,5 @@ install:
test_script: test_script:
# cxxstd=latest (aka 20) breaks VS 2022 in algorithm_project_test so we use cxxstd=17 # cxxstd=latest (aka 20) breaks VS 2022 in algorithm_project_test so we use cxxstd=17
- ..\..\b2 %B2_OPTS% cxxstd=17 test//minimal test//serialization # msvc-14.3 breaks test//serialization, more precisely axis_variant_serialization_test
- ..\..\b2 %B2_OPTS% cxxstd=17 test//minimal

1
.gitignore vendored
View File

@ -11,3 +11,4 @@ __pycache__
tools/lcov-* tools/lcov-*
tools/codecov tools/codecov
coverage-report coverage-report
.cache

View File

@ -7,7 +7,7 @@ include(BoostFetch)
# setup for google benchmark # setup for google benchmark
set(CMAKE_BUILD_TYPE Release) # ok, only set in local scope set(CMAKE_BUILD_TYPE Release) # ok, only set in local scope
option(BENCHMARK_ENABLE_TESTING "" OFF) option(BENCHMARK_ENABLE_TESTING "" OFF)
boost_fetch(google/benchmark) boost_fetch(google/benchmark TAG main)
function(add_benchmark NAME) function(add_benchmark NAME)

View File

@ -19,12 +19,14 @@ template <class Axis>
class iterator : public detail::iterator_adaptor<iterator<Axis>, index_type, class iterator : public detail::iterator_adaptor<iterator<Axis>, index_type,
decltype(std::declval<Axis>().bin(0))> { decltype(std::declval<Axis>().bin(0))> {
public: public:
using reference = typename iterator::iterator_adaptor_::reference;
/// Make iterator from axis and index. /// Make iterator from axis and index.
iterator(const Axis& axis, index_type idx) iterator(const Axis& axis, index_type idx)
: iterator::iterator_adaptor_(idx), axis_(axis) {} : iterator::iterator_adaptor_(idx), axis_(axis) {}
/// Return current bin object. /// Return current bin object.
decltype(auto) operator*() const { return axis_.bin(this->base()); } reference operator*() const { return axis_.bin(this->base()); }
private: private:
const Axis& axis_; const Axis& axis_;

View File

@ -10,6 +10,7 @@
#ifndef BOOST_HISTOGRAM_DETAIL_ITERATOR_ADAPTOR_HPP #ifndef BOOST_HISTOGRAM_DETAIL_ITERATOR_ADAPTOR_HPP
#define BOOST_HISTOGRAM_DETAIL_ITERATOR_ADAPTOR_HPP #define BOOST_HISTOGRAM_DETAIL_ITERATOR_ADAPTOR_HPP
#include <cstddef>
#include <iterator> #include <iterator>
#include <memory> #include <memory>
#include <type_traits> #include <type_traits>
@ -24,26 +25,43 @@ namespace detail {
// produce an lvalue to which a pointer can be formed. We do that by // produce an lvalue to which a pointer can be formed. We do that by
// returning a proxy object containing an instance of the reference object. // returning a proxy object containing an instance of the reference object.
template <class Reference> template <class Reference>
struct operator_arrow_dispatch_t // proxy references struct operator_arrow_dispatch_t {
{ struct pointer {
struct proxy { explicit pointer(Reference const& x) noexcept : m_ref(x) {}
explicit proxy(Reference const& x) noexcept : m_ref(x) {}
Reference* operator->() noexcept { return std::addressof(m_ref); } Reference* operator->() noexcept { return std::addressof(m_ref); }
Reference m_ref; Reference m_ref;
}; };
using result_type = proxy; using result_type = pointer;
static result_type apply(Reference const& x) noexcept { return proxy(x); } static result_type apply(Reference const& x) noexcept { return pointer(x); }
}; };
// specialization for "real" references
template <class T> template <class T>
struct operator_arrow_dispatch_t<T&> // "real" references struct operator_arrow_dispatch_t<T&> {
{
using result_type = T*; using result_type = T*;
static result_type apply(T& x) noexcept { return std::addressof(x); } static result_type apply(T& x) noexcept { return std::addressof(x); }
}; };
// only for random access Base // it is ok if void_t is already defined in another header
template <class...>
using void_t = void;
template <class T, class = void>
struct get_difference_type_impl : std::make_signed<T> {};
template <class T>
struct get_difference_type_impl<
T, void_t<typename std::iterator_traits<T>::difference_type>> {
using type = typename std::iterator_traits<T>::difference_type;
};
template <class T>
using get_difference_type = typename get_difference_type_impl<T>::type;
// adaptor supports only random access Base
// Base: underlying base type of the iterator; can be iterator, pointer, integer
// Reference: type returned when pointer is dereferenced
template <class Derived, class Base, class Reference = std::remove_pointer_t<Base>&, template <class Derived, class Base, class Reference = std::remove_pointer_t<Base>&,
class Value = std::decay_t<Reference>> class Value = std::decay_t<Reference>>
class iterator_adaptor { class iterator_adaptor {
@ -53,103 +71,98 @@ public:
using base_type = Base; using base_type = Base;
using reference = Reference; using reference = Reference;
using value_type = std::remove_const_t<Value>; using value_type = Value;
using pointer = typename operator_arrow_dispatch::result_type; using pointer = typename operator_arrow_dispatch::result_type;
using difference_type = std::ptrdiff_t; using difference_type = get_difference_type<base_type>;
using iterator_category = std::random_access_iterator_tag; using iterator_category = std::random_access_iterator_tag;
iterator_adaptor() = default; iterator_adaptor() = default;
explicit iterator_adaptor(base_type const& iter) : iter_(iter) {} explicit iterator_adaptor(base_type const& iter) : iter_(iter) {}
pointer operator->() const noexcept { // you can override this in derived
return operator_arrow_dispatch::apply(this->derived().operator*()); decltype(auto) operator*() const noexcept { return *iter_; }
}
reference operator[](difference_type n) const { return *(this->derived() + n); }
Derived& operator++() {
++iter_;
return this->derived();
}
Derived& operator--() {
--iter_;
return this->derived();
}
Derived operator++(int) {
Derived tmp(this->derived());
++iter_;
return tmp;
}
Derived operator--(int) {
Derived tmp(this->derived());
--iter_;
return tmp;
}
// you can override this in derived
Derived& operator+=(difference_type n) { Derived& operator+=(difference_type n) {
iter_ += n; iter_ += n;
return this->derived(); return this->derived();
} }
Derived& operator-=(difference_type n) { // you should override this in derived if there is an override for operator+=
iter_ -= n;
return this->derived();
}
Derived operator+(difference_type n) const {
Derived tmp(this->derived());
tmp += n;
return tmp;
}
Derived operator-(difference_type n) const { return operator+(-n); }
template <class... Ts> template <class... Ts>
difference_type operator-(const iterator_adaptor<Ts...>& x) const noexcept { difference_type operator-(const iterator_adaptor<Ts...>& x) const noexcept {
return iter_ - x.iter_; return iter_ - x.iter_;
} }
// you can override this in derived
template <class... Ts> template <class... Ts>
bool operator==(const iterator_adaptor<Ts...>& x) const noexcept { bool operator==(const iterator_adaptor<Ts...>& x) const noexcept {
return iter_ == x.iter_; return iter_ == x.iter_;
} }
reference operator[](difference_type n) const { return *(this->derived() + n); }
pointer operator->() const noexcept {
return operator_arrow_dispatch::apply(this->derived().operator*());
}
Derived& operator-=(difference_type n) { return this->derived().operator+=(-n); }
Derived& operator++() { return this->derived().operator+=(1); }
Derived& operator--() { return this->derived().operator+=(-1); }
Derived operator++(int) {
Derived tmp(this->derived());
operator++();
return tmp;
}
Derived operator--(int) {
Derived tmp(this->derived());
operator--();
return tmp;
}
Derived operator+(difference_type n) const { return Derived(this->derived()) += n; }
Derived operator-(difference_type n) const { return Derived(this->derived()) -= n; }
template <class... Ts> template <class... Ts>
bool operator!=(const iterator_adaptor<Ts...>& x) const noexcept { bool operator!=(const iterator_adaptor<Ts...>& x) const noexcept {
return !this->derived().operator==(x); // equal operator may be overridden in derived return !this->derived().operator==(x);
} }
template <class... Ts> template <class... Ts>
bool operator<(const iterator_adaptor<Ts...>& x) const noexcept { bool operator<(const iterator_adaptor<Ts...>& x) const noexcept {
return iter_ < x.iter_; return iter_ < x.iter_;
} }
template <class... Ts>
bool operator>=(const iterator_adaptor<Ts...>& x) const noexcept {
return iter_ >= x.iter_;
}
template <class... Ts> template <class... Ts>
bool operator>(const iterator_adaptor<Ts...>& x) const noexcept { bool operator>(const iterator_adaptor<Ts...>& x) const noexcept {
return iter_ > x.iter_; return iter_ > x.iter_;
} }
template <class... Ts> template <class... Ts>
bool operator<=(const iterator_adaptor<Ts...>& x) const noexcept { bool operator<=(const iterator_adaptor<Ts...>& x) const noexcept {
return iter_ <= x.iter_; return iter_ <= x.iter_;
} }
template <class... Ts>
bool operator>=(const iterator_adaptor<Ts...>& x) const noexcept {
return iter_ >= x.iter_;
}
friend Derived operator+(difference_type n, const Derived& x) { return x + n; } friend Derived operator+(difference_type n, const Derived& x) { return x + n; }
Base const& base() const noexcept { return iter_; } Base const& base() const noexcept { return iter_; }
protected: protected:
// for convenience in derived classes // for convenience: refer to base class in derived class
using iterator_adaptor_ = iterator_adaptor; using iterator_adaptor_ = iterator_adaptor;
private: private:
Derived& derived() noexcept { return *static_cast<Derived*>(this); } Derived& derived() noexcept { return *static_cast<Derived*>(this); }
const Derived& derived() const noexcept { return *static_cast<Derived const*>(this); } const Derived& derived() const noexcept { return *static_cast<Derived const*>(this); }
Base iter_; base_type iter_;
template <class, class, class, class> template <class, class, class, class>
friend class iterator_adaptor; friend class iterator_adaptor;
@ -159,4 +172,4 @@ private:
} // namespace histogram } // namespace histogram
} // namespace boost } // namespace boost
#endif #endif

View File

@ -17,7 +17,6 @@
#include "std_ostream.hpp" #include "std_ostream.hpp"
#include "utility_iterator.hpp" #include "utility_iterator.hpp"
using namespace boost::histogram;
using boost::histogram::detail::iterator_adaptor; using boost::histogram::detail::iterator_adaptor;
typedef std::deque<int> storage; typedef std::deque<int> storage;
@ -186,5 +185,34 @@ int main() {
BOOST_TEST(i - k == 0); BOOST_TEST(i - k == 0);
} }
{
using C = std::vector<int>;
C a = {{1, 2, 3, 4}};
struct skip_iterator : iterator_adaptor<skip_iterator, C::iterator> {
using difference_type = typename iterator_adaptor_::difference_type;
using iterator_adaptor_::iterator_adaptor_;
skip_iterator& operator+=(difference_type n) {
iterator_adaptor_::operator+=(n * 2);
return *this;
}
};
{
skip_iterator it(a.begin());
BOOST_TEST_EQ(it.base() - a.begin(), 0);
BOOST_TEST_EQ(*it++, 1);
BOOST_TEST_EQ(*it++, 3);
BOOST_TEST_EQ(a.end() - it.base(), 0);
}
{
int i = 0;
for (auto it = skip_iterator(a.begin()); it != skip_iterator(a.end()); ++it) ++i;
BOOST_TEST_EQ(i, 2);
}
}
return boost::report_errors(); return boost::report_errors();
} }

View File

@ -17,9 +17,6 @@
#include <iterator> #include <iterator>
#include <type_traits> #include <type_traits>
namespace boost {
namespace histogram {
namespace detail { namespace detail {
template <class T> template <class T>
struct icast_identity { struct icast_identity {
@ -53,14 +50,14 @@ void trivial_iterator_test(const Iterator i, const Iterator j, T val) {
BOOST_TEST(i != j); BOOST_TEST(i != j);
typename std::iterator_traits<Iterator>::value_type v = *i; typename std::iterator_traits<Iterator>::value_type v = *i;
BOOST_TEST(v == val); BOOST_TEST(v == val);
ignore_unused(v); boost::ignore_unused(v);
BOOST_TEST(v == i->foo()); BOOST_TEST(v == i->foo());
k = i; k = i;
BOOST_TEST(k == k); BOOST_TEST(k == k);
BOOST_TEST(k == i); BOOST_TEST(k == i);
BOOST_TEST(k != j); BOOST_TEST(k != j);
BOOST_TEST(*k == val); BOOST_TEST(*k == val);
ignore_unused(k); boost::ignore_unused(k);
} }
// Preconditions: i != j // Preconditions: i != j
@ -94,7 +91,7 @@ void input_iterator_test(Iterator i, T v1, T v2) {
// we cannot test for equivalence of (void)++i & (void)i++ // we cannot test for equivalence of (void)++i & (void)i++
// as i is only guaranteed to be single pass. // as i is only guaranteed to be single pass.
BOOST_TEST(*i++ == v1); BOOST_TEST(*i++ == v1);
ignore_unused(i1); boost::ignore_unused(i1);
i1 = i; i1 = i;
@ -103,7 +100,7 @@ void input_iterator_test(Iterator i, T v1, T v2) {
BOOST_TEST(*i1 == v2); BOOST_TEST(*i1 == v2);
BOOST_TEST(*i == v2); BOOST_TEST(*i == v2);
ignore_unused(i1); boost::ignore_unused(i1);
// i is dereferencable, so it must be incrementable. // i is dereferencable, so it must be incrementable.
++i; ++i;
@ -176,7 +173,7 @@ void random_access_iterator_test(Iterator i, int N, TrueVals vals) {
int c; int c;
typedef typename std::iterator_traits<Iterator>::value_type value_type; typedef typename std::iterator_traits<Iterator>::value_type value_type;
ignore_unused<value_type>(); boost::ignore_unused<value_type>();
for (c = 0; c < N - 1; ++c) { for (c = 0; c < N - 1; ++c) {
BOOST_TEST(i == j + c); BOOST_TEST(i == j + c);
@ -197,7 +194,7 @@ void random_access_iterator_test(Iterator i, int N, TrueVals vals) {
BOOST_TEST(*i == vals[N - 1 - c]); BOOST_TEST(*i == vals[N - 1 - c]);
BOOST_TEST(*i == implicit_cast<value_type>(j[N - 1 - c])); BOOST_TEST(*i == implicit_cast<value_type>(j[N - 1 - c]));
Iterator q = k - c; Iterator q = k - c;
ignore_unused(q); boost::ignore_unused(q);
BOOST_TEST(*i == *q); BOOST_TEST(*i == *q);
BOOST_TEST(i > j); BOOST_TEST(i > j);
BOOST_TEST(i >= j); BOOST_TEST(i >= j);
@ -220,10 +217,7 @@ void const_nonconst_iterator_test(Iterator i, ConstIterator j) {
k = i; k = i;
BOOST_TEST(k == i); BOOST_TEST(k == i);
BOOST_TEST(i == k); BOOST_TEST(i == k);
ignore_unused(k); boost::ignore_unused(k);
} }
} // namespace histogram
} // namespace boost
#endif // BOOST_HISTOGRAM_TEST_ITERATOR_TESTS_HPP #endif // BOOST_HISTOGRAM_TEST_ITERATOR_TESTS_HPP