Nui
selectables_registry.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include <nui/concepts.hpp>
4 
5 #include <vector>
6 #include <utility>
7 #include <optional>
8 #include <algorithm>
9 #include <variant>
10 #include <limits>
11 #include <set>
12 #include <iterator>
13 
14 namespace Nui
15 {
16  namespace Detail
17  {
18  struct InplaceTag
19  {};
20  }
21 
30  template <typename T>
32  {
33  public:
35  using IdType = std::size_t;
36 
40  struct ItemWithId
41  {
44 
50  std::optional<T> item;
51 
52  template <typename... Args>
53  ItemWithId(IdType id, Detail::InplaceTag, Args&&... args)
54  : id{id}
55  , item{T{std::forward<Args>(args)...}}
56  {}
58  : id{id}
59  , item{std::move(item)}
60  {}
61  ItemWithId(IdType id, std::optional<T> item)
62  : id{id}
63  , item{std::move(item)}
64  {}
65  ItemWithId(ItemWithId const&) = default;
66  ItemWithId(ItemWithId&&) = default;
67  ItemWithId& operator=(ItemWithId const&) = default;
69 
71  bool operator<(ItemWithId const& other) const
72  {
73  return id < other.id;
74  }
75  };
76 
78  using ItemContainerType = std::vector<ItemWithId>;
79 
81  constexpr static auto invalidId = std::numeric_limits<IdType>::max();
82 
90  template <typename WrappedIterator>
92  {
93  public:
94  IteratorBase(WrappedIterator wrapped, WrappedIterator begin, WrappedIterator end)
95  : wrappedIterator_{std::move(wrapped)}
96  , beginIterator_{std::move(begin)}
97  , endIterator_{std::move(end)}
98  {
100  return;
101  // shift iterator to first valid item
102  while (wrappedIterator_ != endIterator_ && !wrappedIterator_->item)
104  }
105  IteratorBase(IteratorBase const&) = default;
107  IteratorBase& operator=(IteratorBase const&) = default;
109  ~IteratorBase() = default;
110 
112  {
114  while (wrappedIterator_ != endIterator_ && !wrappedIterator_->item)
116  return *this;
117  }
118 
120  {
121  IteratorBase tmp = *this;
122  ++*this;
123  return tmp;
124  }
125 
127  {
133  return *this;
134  }
135 
137  {
138  IteratorBase tmp = *this;
139  --*this;
140  return tmp;
141  }
142 
143  friend bool operator==(const IteratorBase& lhs, const IteratorBase& rhs)
144  {
145  return lhs.wrappedIterator_ == rhs.wrappedIterator_;
146  }
147 
148  friend bool operator!=(const IteratorBase& lhs, const IteratorBase& rhs)
149  {
150  return !(lhs == rhs);
151  }
152 
153  IteratorBase operator+(std::size_t offset) const
154  {
155  IteratorType tmp = *this;
156  for (std::size_t i = 0; i < offset && tmp != endIterator_; ++i)
157  ++tmp;
158  return tmp;
159  }
160 
161  IteratorBase operator-(std::size_t offset) const
162  {
163  IteratorType tmp = *this;
164  for (std::size_t i = 0; i < offset && tmp != beginIterator_; ++i)
165  --tmp;
166  return tmp;
167  }
168 
169  IteratorBase& operator+=(std::size_t offset)
170  {
171  for (std::size_t i = 0; i < offset && *this != endIterator_; ++i)
172  ++*this;
173  }
174 
175  IteratorBase& operator-=(std::size_t offset)
176  {
177  for (std::size_t i = 0; i < offset && *this != beginIterator_; ++i)
178  --*this;
179  }
180 
181  bool isEnd() const
182  {
183  return wrappedIterator_ == endIterator_;
184  }
185 
186  protected:
187  WrappedIterator wrappedIterator_;
188  WrappedIterator beginIterator_;
189  WrappedIterator endIterator_;
190  };
191 
192  template <typename WrappedIterator>
193  class ConstIterator : public IteratorBase<WrappedIterator>
194  {
195  public:
199 
200  ConstIterator(typename ItemContainerType::iterator iter)
201  : IteratorBase<WrappedIterator>{std::move(iter)}
202  {}
203 
204  auto const& operator*() const
205  {
207  throw std::runtime_error{"Dereferencing end iterator"};
208  return wrappedIterator_->item.value();
209  }
210 
211  auto const* operator->() const
212  {
214  throw std::runtime_error{"Dereferencing end iterator"};
215  return &wrappedIterator_->item.value();
216  }
217  };
218 
219  template <typename WrappedIterator>
220  class Iterator : public IteratorBase<WrappedIterator>
221  {
222  public:
226 
227  auto& operator*()
228  {
230  throw std::runtime_error{"Dereferencing end iterator"};
231  return wrappedIterator_->item.value();
232  }
233 
234  auto* operator->()
235  {
237  throw std::runtime_error{"Dereferencing end iterator"};
238  return &wrappedIterator_->item.value();
239  }
240  };
241 
244 
245  public:
246  SelectablesRegistry() = default;
251  ~SelectablesRegistry() = default;
252 
259  IdType append(T const& element)
260  {
261  items_.push_back(ItemWithId{id_, std::optional<T>{element}});
262  ++itemCount_;
263  const auto id = id_;
264  id_++;
265  if (id_ == invalidId - 1)
266  id_ = 0;
267  return id;
268  }
269 
276  IdType append(T&& element)
277  {
278  items_.push_back(ItemWithId{id_, std::optional<T>{std::move(element)}});
279  ++itemCount_;
280  const auto id = id_;
281  id_++;
282  if (id_ == invalidId - 1)
283  id_ = 0;
284  return id;
285  }
286 
294  template <typename... Args>
295  IdType emplace(Args&&... args)
296  {
297  items_.push_back(ItemWithId{id_, Detail::InplaceTag{}, std::forward<Args>(args)...});
298  ++itemCount_;
299  const auto id = id_;
300  id_++;
301  if (id_ == invalidId - 1)
302  id_ = 0;
303  return id;
304  }
305 
313  {
314  const auto p = findItem(id);
315  if (p == std::end(items_))
316  return end();
317 
318  p->item.reset();
319  --itemCount_;
320 
321  auto result = condense();
322  return {result, items_.begin(), items_.end()};
323  }
324 
331  std::optional<T> pop(IdType id)
332  {
333  const auto p = findItem(id);
334  if (p == std::end(items_))
335  return std::nullopt;
336 
337  const auto result = std::move(p->item);
338  p->item.reset();
339  --itemCount_;
340 
341  condense();
342  return result;
343  }
344 
346  {
348  std::optional<T>* item;
349 
351  bool found;
352 
355  };
356 
364  {
365  const auto iter = findItem(id);
366  if (iter == std::end(items_))
367  return {nullptr, false, false};
368  if (!iter->item.has_value())
369  return {nullptr, true, true};
370 
371  --itemCount_;
372 
373  const auto selectedIter = selected_.insert(std::move(*iter)).first;
374  iter->item.reset();
375  // having modifying access to the optional<T> does not mess with the set ordering. const casting is fine
376  // here.
377  return {
378  .item = &(const_cast<ItemWithId&>(*selectedIter).item),
379  .found = true,
380  .alreadySelected = false,
381  };
382  }
383 
390  std::size_t deselectAll(std::invocable<ItemWithId const&> auto const& callback)
391  {
392  std::size_t result = 0;
393  for (auto const& selected : selected_)
394  {
395  auto const id = selected.id;
396  if (callback(selected))
397  {
398  auto entry = findItem(id);
399  if (entry != std::end(items_))
400  {
401  ++itemCount_;
402  ++result;
403  entry->item = std::move(const_cast<ItemWithId&>(selected).item);
404  }
405  }
406  }
407  selected_.clear();
408  condense();
409  return result;
410  }
411 
420  bool deselect(IdType id, std::invocable<ItemWithId const&> auto const& callback)
421  {
422  auto const iter = selected_.find(ItemWithId{id, std::nullopt});
423  if (iter == std::end(selected_))
424  return false;
425 
426  if (callback(*iter))
427  {
428  auto entry = findItem(id);
429  if (entry != std::end(items_))
430  {
431  ++itemCount_;
432  entry->item = std::move(const_cast<ItemWithId&>(*iter).item);
433  }
434  return true;
435  }
436  selected_.erase(iter);
437  if (selected_.empty())
438  condense();
439  return false;
440  }
441 
442  void clear()
443  {
444  items_.clear();
445  selected_.clear();
446  itemCount_ = 0;
447  id_ = 0;
448  }
449 
457  {
458  auto iter = findItem(id);
459  if (iter == std::end(items_))
460  return end();
461  return IteratorType{iter, items_.begin(), items_.end()};
462  }
463 
471  {
472  auto iter = findItem(id);
473  if (iter == std::end(items_))
474  return end();
475  return ConstIteratorType{iter, items_.begin(), items_.end()};
476  }
477 
484  auto const& operator[](IdType id) const
485  {
486  return *get(id);
487  }
488 
495  auto& operator[](IdType id)
496  {
497  return *get(id);
498  }
499 
504  {
505  return {items_.begin(), items_.begin(), items_.end()};
506  }
507 
512  {
513  return {items_.begin(), items_.begin(), items_.end()};
514  }
515 
520  {
521  return {items_.cbegin(), items_.begin(), items_.end()};
522  }
523 
528  {
529  return {items_.end(), items_.begin(), items_.end()};
530  }
531 
536  {
537  return {items_.end(), items_.begin(), items_.end()};
538  }
539 
544  {
545  return {items_.cend(), items_.begin(), items_.end()};
546  }
547 
551  bool empty() const
552  {
553  return itemCount_ == 0;
554  }
555 
559  std::size_t size() const
560  {
561  return itemCount_;
562  }
563 
567  typename ItemContainerType::iterator rawBegin()
568  {
569  return items_.begin();
570  }
571 
575  typename ItemContainerType::iterator rawEnd()
576  {
577  return items_.end();
578  }
579 
583  typename ItemContainerType::const_iterator rawBegin() const
584  {
585  return items_.begin();
586  }
587 
591  typename ItemContainerType::const_iterator rawConstBegin() const
592  {
593  return items_.cbegin();
594  }
595 
599  typename ItemContainerType::const_iterator rawEnd() const
600  {
601  return items_.end();
602  }
603 
607  typename ItemContainerType::const_iterator rawConstEnd() const
608  {
609  return items_.cend();
610  }
611 
612  template <typename RegistryPtr>
614  {
615  RegistryPtr registry;
616  typename ItemContainerType::iterator begin() const
617  {
618  return registry->rawBegin();
619  }
620  typename ItemContainerType::iterator end() const
621  {
622  return registry->rawEnd();
623  }
624  typename ItemContainerType::iterator cbegin() const
625  {
626  return registry->rawConstBegin();
627  }
628  typename ItemContainerType::iterator cend() const
629  {
630  return registry->rawConstEnd();
631  }
632  };
633 
640  {
641  return {this};
642  }
643 
650  {
651  return {this};
652  }
653 
654  private:
655  typename ItemContainerType::iterator findItem(IdType id)
656  {
657  const auto p =
658  std::lower_bound(std::begin(items_), std::end(items_), id, [](auto const& lhs, auto const& rhs) {
659  return lhs.id < rhs;
660  });
661 
662  if (p == std::end(items_) || p->id != id)
663  return std::end(items_);
664  return p;
665  }
666  typename ItemContainerType::const_iterator findItem(IdType id) const
667  {
668  const auto p =
669  std::lower_bound(std::begin(items_), std::end(items_), id, [](auto const& lhs, auto const& rhs) {
670  return lhs.id < rhs;
671  });
672 
673  if (p == std::end(items_) || p->id != id)
674  return std::end(items_);
675  return p;
676  }
677 
678  auto condense()
679  {
680  if (selected_.empty() && itemCount_ < (items_.size() / 2))
681  {
682  return items_.erase(
683  std::remove_if(
684  std::begin(items_),
685  std::end(items_),
686  [](auto const& item) {
687  return !item.item;
688  }),
689  std::end(items_));
690  }
691  return std::end(items_);
692  }
693 
694  private:
695  ItemContainerType items_{};
696  // TODO: improve performance, id link backs are costly, each one is a binary search.
697  std::set<ItemWithId> selected_{};
698  IdType itemCount_{0};
699  IdType id_{0};
700  };
701 }
Definition: selectables_registry.hpp:194
auto const * operator->() const
Definition: selectables_registry.hpp:211
auto const & operator*() const
Definition: selectables_registry.hpp:204
ConstIterator(typename ItemContainerType::iterator iter)
Definition: selectables_registry.hpp:200
Iterator that ignores items that are selected.
Definition: selectables_registry.hpp:92
IteratorBase & operator=(IteratorBase const &)=default
bool isEnd() const
Definition: selectables_registry.hpp:181
WrappedIterator beginIterator_
Definition: selectables_registry.hpp:188
IteratorBase & operator++()
Definition: selectables_registry.hpp:111
IteratorBase(IteratorBase const &)=default
IteratorBase operator+(std::size_t offset) const
Definition: selectables_registry.hpp:153
friend bool operator!=(const IteratorBase &lhs, const IteratorBase &rhs)
Definition: selectables_registry.hpp:148
IteratorBase & operator-=(std::size_t offset)
Definition: selectables_registry.hpp:175
IteratorBase & operator--()
Definition: selectables_registry.hpp:126
IteratorBase(WrappedIterator wrapped, WrappedIterator begin, WrappedIterator end)
Definition: selectables_registry.hpp:94
friend bool operator==(const IteratorBase &lhs, const IteratorBase &rhs)
Definition: selectables_registry.hpp:143
WrappedIterator endIterator_
Definition: selectables_registry.hpp:189
IteratorBase & operator+=(std::size_t offset)
Definition: selectables_registry.hpp:169
IteratorBase operator-(std::size_t offset) const
Definition: selectables_registry.hpp:161
WrappedIterator wrappedIterator_
Definition: selectables_registry.hpp:187
IteratorBase(IteratorBase &&)=default
IteratorBase operator--(int)
Definition: selectables_registry.hpp:136
IteratorBase & operator=(IteratorBase &&)=default
IteratorBase operator++(int)
Definition: selectables_registry.hpp:119
Definition: selectables_registry.hpp:221
auto * operator->()
Definition: selectables_registry.hpp:234
auto & operator*()
Definition: selectables_registry.hpp:227
This container associates items with an id and allows for individual items to be "selected",...
Definition: selectables_registry.hpp:32
ConstIteratorType cend() const
Returns end iterator.
Definition: selectables_registry.hpp:543
ItemContainerType::const_iterator rawConstEnd() const
Returns a const iterator to the underlying container.
Definition: selectables_registry.hpp:607
constexpr static auto invalidId
Invalid id value.
Definition: selectables_registry.hpp:81
ItemContainerType::const_iterator rawBegin() const
Returns a const iterator to the underlying container.
Definition: selectables_registry.hpp:583
IteratorType begin()
Returns iterator to first unselected item or end.
Definition: selectables_registry.hpp:503
IteratorType erase(IdType id)
Erase/Remove an item from the container.
Definition: selectables_registry.hpp:312
auto const & operator[](IdType id) const
Returns item by id.
Definition: selectables_registry.hpp:484
ConstIteratorType begin() const
Returns iterator to first unselected item or end.
Definition: selectables_registry.hpp:511
IteratorType end()
Returns end iterator.
Definition: selectables_registry.hpp:527
ConstIteratorType end() const
Returns end iterator.
Definition: selectables_registry.hpp:535
SelectablesRegistry(SelectablesRegistry &&)=default
ItemContainerType::const_iterator rawConstBegin() const
Returns a const iterator to the underlying container.
Definition: selectables_registry.hpp:591
IdType emplace(Args &&... args)
Emplace an item to the container.
Definition: selectables_registry.hpp:295
SelectablesRegistry & operator=(const SelectablesRegistry &)=default
std::size_t IdType
Id type used to identify items.
Definition: selectables_registry.hpp:35
std::size_t deselectAll(std::invocable< ItemWithId const & > auto const &callback)
Deselects all items.
Definition: selectables_registry.hpp:390
ItemContainerType::iterator rawBegin()
Returns an iterator to the underlying container.
Definition: selectables_registry.hpp:567
ConstIteratorType cbegin() const
Returns iterator to first unselected item or end.
Definition: selectables_registry.hpp:519
std::optional< T > pop(IdType id)
Erase/Remove an item from the container and return it.
Definition: selectables_registry.hpp:331
bool deselect(IdType id, std::invocable< ItemWithId const & > auto const &callback)
Deselects item with id.
Definition: selectables_registry.hpp:420
SelectionResult select(IdType id)
Select an item.
Definition: selectables_registry.hpp:363
SelectablesRegistry(const SelectablesRegistry &)=default
ItemContainerType::iterator rawEnd()
Returns an iterator to the underlying container.
Definition: selectables_registry.hpp:575
IdType append(T &&element)
Append an item to the container.
Definition: selectables_registry.hpp:276
std::vector< ItemWithId > ItemContainerType
Type of the container that stores the items.
Definition: selectables_registry.hpp:78
SelectablesRegistry & operator=(SelectablesRegistry &&)=default
RawRangeWrap< SelectablesRegistry< T > const * > rawRange() const
Helper for range based for loops.
Definition: selectables_registry.hpp:649
bool empty() const
Returns whether the container is empty.
Definition: selectables_registry.hpp:551
IteratorType get(IdType id)
Get iterator to item with id.
Definition: selectables_registry.hpp:456
RawRangeWrap< SelectablesRegistry< T > * > rawRange()
Helper for range based for loops.
Definition: selectables_registry.hpp:639
ItemContainerType::const_iterator rawEnd() const
Returns a const iterator to the underlying container.
Definition: selectables_registry.hpp:599
ConstIteratorType get(IdType id) const
Get iterator to item with id.
Definition: selectables_registry.hpp:470
IdType append(T const &element)
Append an item to the container.
Definition: selectables_registry.hpp:259
auto & operator[](IdType id)
Returns item by id.
Definition: selectables_registry.hpp:495
void clear()
Definition: selectables_registry.hpp:442
std::size_t size() const
Returns the amount of items in the container.
Definition: selectables_registry.hpp:559
Definition: file_dialog.hpp:6
Definition: selectables_registry.hpp:19
Wrapper around items that associates them with an id.
Definition: selectables_registry.hpp:41
ItemWithId(IdType id, std::optional< T > item)
Definition: selectables_registry.hpp:61
ItemWithId & operator=(ItemWithId &&)=default
std::optional< T > item
The item.
Definition: selectables_registry.hpp:50
ItemWithId(IdType id, T item)
Definition: selectables_registry.hpp:57
bool operator<(ItemWithId const &other) const
Compares the id of the item.
Definition: selectables_registry.hpp:71
ItemWithId(IdType id, Detail::InplaceTag, Args &&... args)
Definition: selectables_registry.hpp:53
ItemWithId(ItemWithId &&)=default
ItemWithId & operator=(ItemWithId const &)=default
IdType id
Id of the item.
Definition: selectables_registry.hpp:43
ItemWithId(ItemWithId const &)=default
Definition: selectables_registry.hpp:614
RegistryPtr registry
Definition: selectables_registry.hpp:615
ItemContainerType::iterator end() const
Definition: selectables_registry.hpp:620
ItemContainerType::iterator cend() const
Definition: selectables_registry.hpp:628
ItemContainerType::iterator begin() const
Definition: selectables_registry.hpp:616
ItemContainerType::iterator cbegin() const
Definition: selectables_registry.hpp:624
Definition: selectables_registry.hpp:346
std::optional< T > * item
Pointer to the selected item (may be nullptr).
Definition: selectables_registry.hpp:348
bool found
Whether the item was found.
Definition: selectables_registry.hpp:351
bool alreadySelected
Whether the item was already selected.
Definition: selectables_registry.hpp:354