Skip to content

Commit a428b30

Browse files
[ADT] Refactor StringMap iterators (NFC) (#156137)
StringMap has four iterator classes: - StringMapIterBase - StringMapIterator - StringMapConstIterator - StringMapKeyIterator This patch consolidates the first three into one class, namely StringMapIterBase, adds a boolean template parameter to indicate desired constness, and then use "using" directives to specialize the common class: using const_iterator = StringMapIterBase<ValueTy, true>; using iterator = StringMapIterBase<ValueTy, false>; just like how we simplified DenseMapIterator. Remarks: - This patch drops CRTP and iterator_facade_base for simplicity. For fairly simple forward iterators, iterator_facade_base doesn't buy us much. We just have to write a few "using" directives and operator!= manually. - StringMapIterBase has a SFINAE-based constructor to construct a const iterator from a non-const one just like DenseMapIterator. - We now rely on compiler-generated copy and assignment operators.
1 parent e57f0e9 commit a428b30

File tree

4 files changed

+91
-64
lines changed

4 files changed

+91
-64
lines changed

clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ bool MultipleInheritanceCheck::getInterfaceStatus(const CXXRecordDecl *Node,
3939
bool &IsInterface) const {
4040
assert(Node->getIdentifier());
4141
StringRef Name = Node->getIdentifier()->getName();
42-
llvm::StringMapConstIterator<bool> Pair = InterfaceMap.find(Name);
42+
auto Pair = InterfaceMap.find(Name);
4343
if (Pair == InterfaceMap.end())
4444
return false;
4545
IsInterface = Pair->second;

clang/lib/Driver/OffloadBundler.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1936,8 +1936,7 @@ Error OffloadBundler::UnbundleArchive() {
19361936
/// Write out an archive for each target
19371937
for (auto &Target : BundlerConfig.TargetNames) {
19381938
StringRef FileName = TargetOutputFileNameMap[Target];
1939-
StringMapIterator<std::vector<llvm::NewArchiveMember>> CurArchiveMembers =
1940-
OutputArchivesMap.find(Target);
1939+
auto CurArchiveMembers = OutputArchivesMap.find(Target);
19411940
if (CurArchiveMembers != OutputArchivesMap.end()) {
19421941
if (Error WriteErr = writeArchive(FileName, CurArchiveMembers->getValue(),
19431942
SymtabWritingMode::NormalSymtab,

llvm/include/llvm/ADT/StringMap.h

Lines changed: 35 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121
#include "llvm/Support/PointerLikeTypeTraits.h"
2222
#include <initializer_list>
2323
#include <iterator>
24+
#include <type_traits>
2425

2526
namespace llvm {
2627

27-
template <typename ValueTy> class StringMapConstIterator;
28-
template <typename ValueTy> class StringMapIterator;
28+
template <typename ValueTy, bool IsConst> class StringMapIterBase;
2929
template <typename ValueTy> class StringMapKeyIterator;
3030

3131
/// StringMapImpl - This is the base class of StringMap that is shared among
@@ -217,8 +217,8 @@ class LLVM_ALLOCATORHOLDER_EMPTYBASE StringMap
217217
using value_type = StringMapEntry<ValueTy>;
218218
using size_type = size_t;
219219

220-
using const_iterator = StringMapConstIterator<ValueTy>;
221-
using iterator = StringMapIterator<ValueTy>;
220+
using const_iterator = StringMapIterBase<ValueTy, true>;
221+
using iterator = StringMapIterBase<ValueTy, false>;
222222

223223
iterator begin() { return iterator(TheTable, NumBuckets == 0); }
224224
iterator end() { return iterator(TheTable + NumBuckets, true); }
@@ -431,14 +431,17 @@ class LLVM_ALLOCATORHOLDER_EMPTYBASE StringMap
431431
}
432432
};
433433

434-
template <typename DerivedTy, typename ValueTy>
435-
class StringMapIterBase
436-
: public iterator_facade_base<DerivedTy, std::forward_iterator_tag,
437-
ValueTy> {
438-
protected:
434+
template <typename ValueTy, bool IsConst> class StringMapIterBase {
439435
StringMapEntryBase **Ptr = nullptr;
440436

441437
public:
438+
using iterator_category = std::forward_iterator_tag;
439+
using value_type = StringMapEntry<ValueTy>;
440+
using difference_type = std::ptrdiff_t;
441+
using pointer = std::conditional_t<IsConst, const value_type *, value_type *>;
442+
using reference =
443+
std::conditional_t<IsConst, const value_type &, value_type &>;
444+
442445
StringMapIterBase() = default;
443446

444447
explicit StringMapIterBase(StringMapEntryBase **Bucket,
@@ -448,85 +451,56 @@ class StringMapIterBase
448451
AdvancePastEmptyBuckets();
449452
}
450453

451-
DerivedTy &operator=(const DerivedTy &Other) {
452-
Ptr = Other.Ptr;
453-
return static_cast<DerivedTy &>(*this);
454-
}
455-
456-
friend bool operator==(const DerivedTy &LHS, const DerivedTy &RHS) {
457-
return LHS.Ptr == RHS.Ptr;
458-
}
454+
reference operator*() const { return *static_cast<value_type *>(*Ptr); }
455+
pointer operator->() const { return static_cast<value_type *>(*Ptr); }
459456

460-
DerivedTy &operator++() { // Preincrement
457+
StringMapIterBase &operator++() { // Preincrement
461458
++Ptr;
462459
AdvancePastEmptyBuckets();
463-
return static_cast<DerivedTy &>(*this);
460+
return *this;
464461
}
465462

466-
DerivedTy operator++(int) { // Post-increment
467-
DerivedTy Tmp(Ptr);
463+
StringMapIterBase operator++(int) { // Post-increment
464+
StringMapIterBase Tmp(*this);
468465
++*this;
469466
return Tmp;
470467
}
471468

472-
private:
473-
void AdvancePastEmptyBuckets() {
474-
while (*Ptr == nullptr || *Ptr == StringMapImpl::getTombstoneVal())
475-
++Ptr;
469+
template <bool ToConst,
470+
typename = typename std::enable_if<!IsConst && ToConst>::type>
471+
operator StringMapIterBase<ValueTy, ToConst>() const {
472+
return StringMapIterBase<ValueTy, ToConst>(Ptr, true);
476473
}
477-
};
478-
479-
template <typename ValueTy>
480-
class StringMapConstIterator
481-
: public StringMapIterBase<StringMapConstIterator<ValueTy>,
482-
const StringMapEntry<ValueTy>> {
483-
using base = StringMapIterBase<StringMapConstIterator<ValueTy>,
484-
const StringMapEntry<ValueTy>>;
485-
486-
public:
487-
StringMapConstIterator() = default;
488-
explicit StringMapConstIterator(StringMapEntryBase **Bucket,
489-
bool NoAdvance = false)
490-
: base(Bucket, NoAdvance) {}
491474

492-
const StringMapEntry<ValueTy> &operator*() const {
493-
return *static_cast<const StringMapEntry<ValueTy> *>(*this->Ptr);
475+
friend bool operator==(const StringMapIterBase &LHS,
476+
const StringMapIterBase &RHS) {
477+
return LHS.Ptr == RHS.Ptr;
494478
}
495-
};
496-
497-
template <typename ValueTy>
498-
class StringMapIterator : public StringMapIterBase<StringMapIterator<ValueTy>,
499-
StringMapEntry<ValueTy>> {
500-
using base =
501-
StringMapIterBase<StringMapIterator<ValueTy>, StringMapEntry<ValueTy>>;
502479

503-
public:
504-
StringMapIterator() = default;
505-
explicit StringMapIterator(StringMapEntryBase **Bucket,
506-
bool NoAdvance = false)
507-
: base(Bucket, NoAdvance) {}
508-
509-
StringMapEntry<ValueTy> &operator*() const {
510-
return *static_cast<StringMapEntry<ValueTy> *>(*this->Ptr);
480+
friend bool operator!=(const StringMapIterBase &LHS,
481+
const StringMapIterBase &RHS) {
482+
return !(LHS == RHS);
511483
}
512484

513-
operator StringMapConstIterator<ValueTy>() const {
514-
return StringMapConstIterator<ValueTy>(this->Ptr, true);
485+
private:
486+
void AdvancePastEmptyBuckets() {
487+
while (*Ptr == nullptr || *Ptr == StringMapImpl::getTombstoneVal())
488+
++Ptr;
515489
}
516490
};
517491

518492
template <typename ValueTy>
519493
class StringMapKeyIterator
520494
: public iterator_adaptor_base<StringMapKeyIterator<ValueTy>,
521-
StringMapConstIterator<ValueTy>,
495+
StringMapIterBase<ValueTy, true>,
522496
std::forward_iterator_tag, StringRef> {
523497
using base = iterator_adaptor_base<StringMapKeyIterator<ValueTy>,
524-
StringMapConstIterator<ValueTy>,
498+
StringMapIterBase<ValueTy, true>,
525499
std::forward_iterator_tag, StringRef>;
526500

527501
public:
528502
StringMapKeyIterator() = default;
529-
explicit StringMapKeyIterator(StringMapConstIterator<ValueTy> Iter)
503+
explicit StringMapKeyIterator(StringMapIterBase<ValueTy, true> Iter)
530504
: base(std::move(Iter)) {}
531505

532506
StringRef operator*() const { return this->wrapped()->getKey(); }

llvm/unittests/ADT/StringMapTest.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,4 +693,58 @@ TEST(StringMapCustomTest, StringMapEntrySize) {
693693
EXPECT_EQ(LargeValue, Key.size());
694694
}
695695

696+
TEST(StringMapCustomTest, NonConstIterator) {
697+
StringMap<int> Map;
698+
Map["key"] = 1;
699+
700+
// Check that Map.begin() returns a non-const iterator.
701+
static_assert(
702+
std::is_same_v<decltype(Map.begin()), StringMap<int>::iterator>);
703+
704+
// Check that the value_type of a non-const iterator is not a const type.
705+
static_assert(
706+
!std::is_const_v<StringMap<int>::iterator::value_type>,
707+
"The value_type of a non-const iterator should not be a const type.");
708+
709+
// Check that pointer and reference types are not const.
710+
static_assert(std::is_same_v<StringMap<int>::iterator::pointer,
711+
StringMap<int>::iterator::value_type *>);
712+
static_assert(std::is_same_v<StringMap<int>::iterator::reference,
713+
StringMap<int>::iterator::value_type &>);
714+
715+
// Check that we can construct a const_iterator from an iterator.
716+
static_assert(std::is_constructible_v<StringMap<int>::const_iterator,
717+
StringMap<int>::iterator>);
718+
719+
// Double check that we can actually construct a const_iterator.
720+
StringMap<int>::const_iterator const_it = Map.begin();
721+
(void)const_it;
722+
}
723+
724+
TEST(StringMapCustomTest, ConstIterator) {
725+
StringMap<int> Map;
726+
const StringMap<int> &ConstMap = Map;
727+
728+
// Check that ConstMap.begin() returns a const_iterator.
729+
static_assert(std::is_same_v<decltype(ConstMap.begin()),
730+
StringMap<int>::const_iterator>);
731+
732+
// Check that the value_type of a const iterator is not a const type.
733+
static_assert(
734+
!std::is_const_v<StringMap<int>::const_iterator::value_type>,
735+
"The value_type of a const iterator should not be a const type.");
736+
737+
// Check that pointer and reference types are const.
738+
static_assert(
739+
std::is_same_v<StringMap<int>::const_iterator::pointer,
740+
const StringMap<int>::const_iterator::value_type *>);
741+
static_assert(
742+
std::is_same_v<StringMap<int>::const_iterator::reference,
743+
const StringMap<int>::const_iterator::value_type &>);
744+
745+
// Check that we cannot construct an iterator from a const_iterator.
746+
static_assert(!std::is_constructible_v<StringMap<int>::iterator,
747+
StringMap<int>::const_iterator>);
748+
}
749+
696750
} // end anonymous namespace

0 commit comments

Comments
 (0)