Skip to content

Commit

Permalink
slot_map: Do not require Container to provide reverse_iterator typedefs.
Browse files Browse the repository at this point in the history
`slot_map<T,K,C>` will now propagate the "reverse-iterability" of `C<T>`,
both the existence of the member typedefs and the existence of the
member functions `rbegin`, `rend`, `crbegin`, and `crend` (as a group,
not individually).
  • Loading branch information
Quuxplusone committed Jan 24, 2018
1 parent b35c6f1 commit dbeb876
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 15 deletions.
47 changes: 38 additions & 9 deletions SG14/slot_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#pragma once

#include <iterator>
#include <type_traits>
#include <utility>
#include <vector>
Expand All @@ -37,12 +38,45 @@

namespace stdext {

namespace slot_map_detail {

template<class T> using iterator_category_t = typename std::iterator_traits<T>::iterator_category;
template<class T> struct is_bidirectional_iterator : std::is_convertible<iterator_category_t<T>, std::bidirectional_iterator_tag> {};

template<class...> using void_t = void;
template<class T, class=void> struct has_reverse_iterators : std::false_type {};
template<class T> struct has_reverse_iterators<T, void_t<typename T::reverse_iterator, typename T::const_reverse_iterator>> : std::true_type {};

template<class CRTP, class ContainerT, bool Reversible>
struct reverse_iterator_methods {};

template<class CRTP, class ContainerT>
struct reverse_iterator_methods<CRTP, ContainerT, true> {
using container_type = ContainerT;
using reverse_iterator = typename container_type::reverse_iterator;
using const_reverse_iterator = typename container_type::const_reverse_iterator;

// All begin() and end() variations have O(1) time and space complexity.
//
constexpr reverse_iterator rbegin() { return values().rbegin(); }
constexpr reverse_iterator rend() { return values().rend(); }
constexpr const_reverse_iterator rbegin() const { return values().rbegin(); }
constexpr const_reverse_iterator rend() const { return values().rend(); }
constexpr const_reverse_iterator crbegin() const { return values().rbegin(); }
constexpr const_reverse_iterator crend() const { return values().rend(); }
private:
ContainerT& values() { return static_cast<CRTP*>(this)->values_; }
const ContainerT& values() const { return static_cast<const CRTP*>(this)->values_; }
};

} // namespace slot_map_detail

template<
class T,
class Key = std::pair<unsigned, unsigned>,
template<class...> class Container = std::vector
>
class slot_map
class slot_map : public slot_map_detail::reverse_iterator_methods<slot_map<T, Key, Container>, Container<T>, slot_map_detail::has_reverse_iterators<Container<T>>::value>
{
#if __cplusplus >= 201703L
static constexpr auto get_index(const Key& k) { const auto& [idx, gen] = k; return idx; }
Expand All @@ -58,6 +92,8 @@ class slot_map

using slot_iterator = typename Container<Key>::iterator;

template<class, class, bool> friend struct slot_map_detail::reverse_iterator_methods;

public:
using key_type = Key;
using mapped_type = T;
Expand All @@ -72,13 +108,12 @@ class slot_map
using const_pointer = typename container_type::const_pointer;
using iterator = typename container_type::iterator;
using const_iterator = typename container_type::const_iterator;
using reverse_iterator = typename container_type::reverse_iterator;
using const_reverse_iterator = typename container_type::const_reverse_iterator;

using size_type = typename container_type::size_type;
using value_type = typename container_type::value_type;

static_assert(std::is_same<value_type, mapped_type>::value, "Container<T>::value_type must be identical to T");
static_assert(slot_map_detail::is_bidirectional_iterator<iterator>::value, "Container<T> must have bidirectional iterators");

constexpr slot_map() = default;
constexpr slot_map(const slot_map&) = default;
Expand Down Expand Up @@ -160,12 +195,6 @@ class slot_map
constexpr const_iterator end() const { return values_.end(); }
constexpr const_iterator cbegin() const { return values_.begin(); }
constexpr const_iterator cend() const { return values_.end(); }
constexpr reverse_iterator rbegin() { return values_.rbegin(); }
constexpr reverse_iterator rend() { return values_.rend(); }
constexpr const_reverse_iterator rbegin() const { return values_.rbegin(); }
constexpr const_reverse_iterator rend() const { return values_.rend(); }
constexpr const_reverse_iterator crbegin() const { return values_.rbegin(); }
constexpr const_reverse_iterator crend() const { return values_.rend(); }

// Functions for checking the size and capacity of the adapted container
// have the same complexity as the adapted container.
Expand Down
39 changes: 33 additions & 6 deletions SG14_test/slot_map_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ struct Vector {
using size_type = unsigned;
using iterator = T*;
using const_iterator = const T*;
using reverse_iterator = std::reverse_iterator<T*>;
using const_reverse_iterator = std::reverse_iterator<const T*>;

Vector() = default;
template<class T_ = T, class = std::enable_if_t<std::is_copy_constructible<T_>::value>>
Expand Down Expand Up @@ -347,8 +345,6 @@ static void TypedefTests()
static_assert(std::is_same<typename SM::const_pointer, const int*>::value, "");
static_assert(std::is_same<typename SM::iterator, TestContainer::Vector<int>::iterator>::value, "");
static_assert(std::is_same<typename SM::const_iterator, TestContainer::Vector<int>::const_iterator>::value, "");
static_assert(std::is_same<typename SM::reverse_iterator, TestContainer::Vector<int>::reverse_iterator>::value, "");
static_assert(std::is_same<typename SM::const_reverse_iterator, TestContainer::Vector<int>::const_reverse_iterator>::value, "");
static_assert(std::is_same<typename SM::size_type, unsigned>::value, "");
static_assert(std::is_same<typename SM::value_type, int>::value, "");
}
Expand All @@ -374,12 +370,39 @@ static void TypedefTests()
#endif // __cplusplus >= 201703L
}

template<class SM, class = decltype(std::declval<SM&>().rbegin())>
void VerifyRbeginExists(bool expected)
{
assert(expected);
SM sm;
auto r1 = sm.rbegin();
auto r2 = sm.rend();
auto cr1 = sm.crbegin();
auto cr2 = sm.crend();
assert(r1 == r2);
assert(cr1 == cr2);
static_assert(std::is_same<decltype(r1), typename SM::reverse_iterator>::value, "");
static_assert(std::is_same<decltype(r2), typename SM::reverse_iterator>::value, "");
static_assert(std::is_same<decltype(cr1), typename SM::const_reverse_iterator>::value, "");
static_assert(std::is_same<decltype(cr2), typename SM::const_reverse_iterator>::value, "");
static_assert(std::is_same<typename SM::reverse_iterator, typename SM::container_type::reverse_iterator>::value, "");
static_assert(std::is_same<typename SM::const_reverse_iterator, typename SM::container_type::const_reverse_iterator>::value, "");
}

template<class SM, class Bool>
void VerifyRbeginExists(Bool expected)
{
assert(not expected);

}

void sg14_test::slot_map_test()
{
TypedefTests();

// Test the most basic slot_map.
using slot_map_1 = stdext::slot_map<int>;
VerifyRbeginExists<slot_map_1>(true);
BasicTests<slot_map_1>(42, 37);
FullContainerStressTest<slot_map_1>([]() { return 1; });
InsertEraseStressTest<slot_map_1>([i=3]() mutable { return ++i; });
Expand All @@ -388,6 +411,7 @@ void sg14_test::slot_map_test()

// Test slot_map with a custom key type (C++14 destructuring).
using slot_map_2 = stdext::slot_map<unsigned long, TestKey::key_16_8_t>;
VerifyRbeginExists<slot_map_2>(true);
BasicTests<slot_map_2>(425, 375);
FullContainerStressTest<slot_map_2>([]() { return 42; });
InsertEraseStressTest<slot_map_2>([i=5]() mutable { return ++i; });
Expand All @@ -397,6 +421,7 @@ void sg14_test::slot_map_test()
#if __cplusplus >= 201703L
// Test slot_map with a custom key type (C++17 destructuring).
using slot_map_3 = stdext::slot_map<int, TestKey::key_11_5_t>;
VerifyRbeginExists<slot_map_3>(true);
BasicTests<slot_map_3>(42, 37);
FullContainerStressTest<slot_map_3>([]() { return 42; });
InsertEraseStressTest<slot_map_3>([i=3]() mutable { return ++i; });
Expand All @@ -406,6 +431,7 @@ void sg14_test::slot_map_test()

// Test slot_map with a custom (but standard and random-access) container type.
using slot_map_4 = stdext::slot_map<int, std::pair<unsigned, unsigned>, std::deque>;
VerifyRbeginExists<slot_map_4>(true);
BasicTests<slot_map_4>(415, 315);
FullContainerStressTest<slot_map_4>([]() { return 37; });
InsertEraseStressTest<slot_map_4>([i=7]() mutable { return ++i; });
Expand All @@ -414,6 +440,7 @@ void sg14_test::slot_map_test()

// Test slot_map with a custom (non-standard, random-access) container type.
using slot_map_5 = stdext::slot_map<int, std::pair<unsigned, unsigned>, TestContainer::Vector>;
VerifyRbeginExists<slot_map_5>(false);
BasicTests<slot_map_5>(415, 315);
FullContainerStressTest<slot_map_5>([]() { return 37; });
InsertEraseStressTest<slot_map_5>([i=7]() mutable { return ++i; });
Expand All @@ -422,6 +449,7 @@ void sg14_test::slot_map_test()

// Test slot_map with a custom (standard, bidirectional-access) container type.
using slot_map_6 = stdext::slot_map<int, std::pair<unsigned, unsigned>, std::list>;
VerifyRbeginExists<slot_map_6>(true);
BasicTests<slot_map_6>(415, 315);
FullContainerStressTest<slot_map_6>([]() { return 37; });
InsertEraseStressTest<slot_map_6>([i=7]() mutable { return ++i; });
Expand All @@ -431,6 +459,7 @@ void sg14_test::slot_map_test()
// Test slot_map with a move-only value_type.
// Sadly, standard containers do not propagate move-only-ness, so we must use our custom Vector instead.
using slot_map_7 = stdext::slot_map<std::unique_ptr<int>, std::pair<unsigned, int>, TestContainer::Vector>;
VerifyRbeginExists<slot_map_7>(false);
static_assert(std::is_move_constructible<slot_map_7>::value, "");
static_assert(std::is_move_assignable<slot_map_7>::value, "");
static_assert(not std::is_copy_constructible<slot_map_7>::value, "");
Expand Down Expand Up @@ -459,8 +488,6 @@ concept bool SlotMapContainer =
typename Ctr<T>::const_pointer;
typename Ctr<T>::iterator;
typename Ctr<T>::const_iterator;
typename Ctr<T>::reverse_iterator;
typename Ctr<T>::const_reverse_iterator;
{ c.emplace_back(t) };
{ c.pop_back() };
{ c.begin() } -> typename Ctr<T>::iterator;
Expand Down

0 comments on commit dbeb876

Please sign in to comment.