BLI: improve support for generic algorithms with c++ containers

Some generic algorithms from the standard library like `std::any_of`
did not work with all container and iterator types. To improve the
situation, this patch adds various type members to containers
and iterators.

Custom iterators for Set, Map and IndexRange now have an iterator
category, which soe algorithms require. IndexRange could become
a random access iterator, but adding all the missing methods can
be done when it is necessary.
This commit is contained in:
Jacques Lucke 2021-03-20 15:42:35 +01:00
parent 59d3ec1eef
commit 98721c8543
12 changed files with 197 additions and 3 deletions

View File

@ -60,6 +60,16 @@ template<
*/
typename Allocator = GuardedAllocator>
class Array {
public:
using value_type = T;
using pointer = T *;
using const_pointer = const T *;
using reference = T &;
using const_reference = const T &;
using iterator = T *;
using const_iterator = const T *;
using size_type = int64_t;
private:
/** The beginning of the array. It might point into the inline buffer. */
T *data_;

View File

@ -82,11 +82,18 @@ class IndexRange {
}
class Iterator {
public:
using iterator_category = std::forward_iterator_tag;
using value_type = int64_t;
using pointer = const int64_t *;
using reference = const int64_t &;
using difference_type = std::ptrdiff_t;
private:
int64_t current_;
public:
constexpr Iterator(int64_t current) : current_(current)
constexpr explicit Iterator(int64_t current) : current_(current)
{
}
@ -96,9 +103,21 @@ class IndexRange {
return *this;
}
constexpr bool operator!=(const Iterator &iterator) const
constexpr Iterator operator++(int) const
{
return current_ != iterator.current_;
Iterator copied_iterator = *this;
++copied_iterator;
return copied_iterator;
}
constexpr friend bool operator!=(const Iterator &a, const Iterator &b)
{
return a.current_ != b.current_;
}
constexpr friend bool operator==(const Iterator &a, const Iterator &b)
{
return a.current_ == b.current_;
}
constexpr int64_t operator*() const

View File

@ -120,6 +120,9 @@ template<
*/
typename Allocator = GuardedAllocator>
class Map {
public:
using size_type = int64_t;
private:
/**
* Slots are either empty, occupied or removed. The number of occupied slots can be computed by
@ -623,6 +626,9 @@ class Map {
* This uses the "curiously recurring template pattern" (CRTP).
*/
template<typename SubIterator> struct BaseIterator {
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
Slot *slots_;
int64_t total_slots_;
int64_t current_slot_;
@ -642,6 +648,13 @@ class Map {
return *this;
}
BaseIterator operator++(int) const
{
BaseIterator copied_iterator = *this;
++copied_iterator;
return copied_iterator;
}
friend bool operator!=(const BaseIterator &a, const BaseIterator &b)
{
BLI_assert(a.slots_ == b.slots_);
@ -649,6 +662,11 @@ class Map {
return a.current_slot_ != b.current_slot_;
}
friend bool operator==(const BaseIterator &a, const BaseIterator &b)
{
return !(a != b);
}
SubIterator begin() const
{
for (int64_t i = 0; i < total_slots_; i++) {
@ -672,6 +690,10 @@ class Map {
class KeyIterator final : public BaseIterator<KeyIterator> {
public:
using value_type = Key;
using pointer = const Key *;
using reference = const Key &;
KeyIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
: BaseIterator<KeyIterator>(slots, total_slots, current_slot)
{
@ -685,6 +707,10 @@ class Map {
class ValueIterator final : public BaseIterator<ValueIterator> {
public:
using value_type = Value;
using pointer = const Value *;
using reference = const Value &;
ValueIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
: BaseIterator<ValueIterator>(slots, total_slots, current_slot)
{
@ -698,6 +724,10 @@ class Map {
class MutableValueIterator final : public BaseIterator<MutableValueIterator> {
public:
using value_type = Value;
using pointer = Value *;
using reference = Value &;
MutableValueIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
: BaseIterator<MutableValueIterator>(slots, total_slots, current_slot)
{
@ -726,6 +756,10 @@ class Map {
class ItemIterator final : public BaseIterator<ItemIterator> {
public:
using value_type = Item;
using pointer = Item *;
using reference = Item &;
ItemIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
: BaseIterator<ItemIterator>(slots, total_slots, current_slot)
{
@ -740,6 +774,10 @@ class Map {
class MutableItemIterator final : public BaseIterator<MutableItemIterator> {
public:
using value_type = MutableItem;
using pointer = MutableItem *;
using reference = MutableItem &;
MutableItemIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
: BaseIterator<MutableItemIterator>(slots, total_slots, current_slot)
{

View File

@ -38,6 +38,9 @@
namespace blender {
template<typename Key, typename Value> class MultiValueMap {
public:
using size_type = int64_t;
private:
using MapType = Map<Key, Vector<Value>>;
MapType map_;

View File

@ -119,6 +119,16 @@ template<
*/
typename Allocator = GuardedAllocator>
class Set {
public:
class Iterator;
using value_type = Key;
using pointer = Key *;
using const_pointer = const Key *;
using reference = Key &;
using const_reference = const Key &;
using iterator = Iterator;
using size_type = int64_t;
private:
/**
* Slots are either empty, occupied or removed. The number of occupied slots can be computed by
@ -401,6 +411,13 @@ class Set {
* also change their hash.
*/
class Iterator {
public:
using iterator_category = std::forward_iterator_tag;
using value_type = Key;
using pointer = const Key *;
using reference = const Key &;
using difference_type = std::ptrdiff_t;
private:
const Slot *slots_;
int64_t total_slots_;
@ -422,17 +439,34 @@ class Set {
return *this;
}
Iterator operator++(int) const
{
Iterator copied_iterator = *this;
++copied_iterator;
return copied_iterator;
}
const Key &operator*() const
{
return *slots_[current_slot_].key();
}
const Key *operator->() const
{
return slots_[current_slot_].key();
}
friend bool operator!=(const Iterator &a, const Iterator &b)
{
BLI_assert(a.slots_ == b.slots_);
BLI_assert(a.total_slots_ == b.total_slots_);
return a.current_slot_ != b.current_slot_;
}
friend bool operator==(const Iterator &a, const Iterator &b)
{
return !(a != b);
}
};
Iterator begin() const

View File

@ -85,6 +85,15 @@ namespace blender {
* modified.
*/
template<typename T> class Span {
public:
using value_type = T;
using pointer = T *;
using const_pointer = const T *;
using reference = T &;
using const_reference = const T &;
using iterator = const T *;
using size_type = int64_t;
private:
const T *data_ = nullptr;
int64_t size_ = 0;
@ -459,6 +468,15 @@ template<typename T> class Span {
* MutableSpan.
*/
template<typename T> class MutableSpan {
public:
using value_type = T;
using pointer = T *;
using const_pointer = const T *;
using reference = T &;
using const_reference = const T &;
using iterator = T *;
using size_type = int64_t;
private:
T *data_;
int64_t size_;

View File

@ -80,6 +80,14 @@ template<
*/
typename Allocator = GuardedAllocator>
class Stack {
public:
using value_type = T;
using pointer = T *;
using const_pointer = const T *;
using reference = T &;
using const_reference = const T &;
using size_type = int64_t;
private:
using Chunk = StackChunk<T>;

View File

@ -76,6 +76,16 @@ template<
*/
typename Allocator = GuardedAllocator>
class Vector {
public:
using value_type = T;
using pointer = T *;
using const_pointer = const T *;
using reference = T &;
using const_reference = const T &;
using iterator = T *;
using const_iterator = const T *;
using size_type = int64_t;
private:
/**
* Use pointers instead of storing the size explicitly. This reduces the number of instructions

View File

@ -100,6 +100,16 @@ template<
*/
typename Allocator = GuardedAllocator>
class VectorSet {
public:
using value_type = Key;
using pointer = Key *;
using const_pointer = const Key *;
using reference = Key &;
using const_reference = const Key &;
using iterator = Key *;
using const_iterator = const Key *;
using size_type = int64_t;
private:
/**
* Slots are either empty, occupied or removed. The number of occupied slots can be computed by

View File

@ -147,4 +147,13 @@ TEST(index_range, constexpr_)
BLI_STATIC_ASSERT(range.size() == 1, "");
EXPECT_EQ(compiles[0], 1);
}
TEST(index_range, GenericAlgorithms)
{
IndexRange range{4, 10};
EXPECT_TRUE(std::any_of(range.begin(), range.end(), [](int v) { return v == 6; }));
EXPECT_FALSE(std::any_of(range.begin(), range.end(), [](int v) { return v == 20; }));
EXPECT_EQ(std::count_if(range.begin(), range.end(), [](int v) { return v < 7; }), 3);
}
} // namespace blender::tests

View File

@ -587,6 +587,23 @@ TEST(map, EnumKey)
EXPECT_EQ(map.lookup(TestEnum::B), 10);
}
TEST(map, GenericAlgorithms)
{
Map<int, int> map;
map.add(5, 2);
map.add(1, 4);
map.add(2, 2);
map.add(7, 1);
map.add(8, 6);
EXPECT_TRUE(std::any_of(map.keys().begin(), map.keys().end(), [](int v) { return v == 1; }));
EXPECT_TRUE(std::any_of(map.values().begin(), map.values().end(), [](int v) { return v == 1; }));
EXPECT_TRUE(std::any_of(
map.items().begin(), map.items().end(), [](auto item) { return item.value == 1; }));
EXPECT_EQ(std::count(map.values().begin(), map.values().end(), 2), 2);
EXPECT_EQ(std::count(map.values().begin(), map.values().end(), 4), 1);
EXPECT_EQ(std::count(map.keys().begin(), map.keys().end(), 7), 1);
}
/**
* Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot.
*/

View File

@ -526,6 +526,24 @@ TEST(set, AddExceptions)
EXPECT_EQ(set.size(), 0);
}
TEST(set, ForwardIterator)
{
Set<int> set = {5, 2, 6, 4, 1};
Set<int>::iterator iter1 = set.begin();
int value1 = *iter1;
Set<int>::iterator iter2 = iter1++;
EXPECT_EQ(*iter1, value1);
EXPECT_EQ(*iter2, *(++iter1));
}
TEST(set, GenericAlgorithms)
{
Set<int> set = {1, 20, 30, 40};
EXPECT_FALSE(std::any_of(set.begin(), set.end(), [](int v) { return v == 5; }));
EXPECT_TRUE(std::any_of(set.begin(), set.end(), [](int v) { return v == 30; }));
EXPECT_EQ(std::count(set.begin(), set.end(), 20), 1);
}
/**
* Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot.
*/