API — ICoreEssentials/Containers
The public contract of 12 header(s) under ICoreEssentials/Containers — 8 class/struct definition(s), 117 declaration(s). Each section shows the header's banner and its public (and protected-virtual) surface exactly as the file writes it.
ICoreArray.h#
ICoreEssentials/Containers/ICoreArray.h
ICoreArray#
ICoreArray.h:12 · class · 9 declaration(s)
class ICoreArray {
public:
ICoreArray(const size_t r = 0, const size_t c = 0) : rows(r), cols(c), data(r * c) {}
std::vector<T> getAllEntriesAsList() const {
return data;
}
// Resize explicitly
void resize(const size_t r, const size_t c) {
grow(r, c);
}
// Safe setter/getter with auto-grow
T& operator()(const size_t i, size_t j) {
if (i >= rows || j >= cols) {
grow(std::max(rows, i + 1), std::max(cols, j + 1));
}
return data[i * cols + j];
}
const T& operator()(size_t i, size_t j) const {
return data[i * cols + j]; // assume valid for const
}
ICoreArray<T> slice(const size_t r1, const size_t r2, const size_t c1, const size_t c2) const {
ICoreArray<T> result(r2 - r1 + 1, c2 - c1 + 1);
for (size_t i = r1; i <= r2; ++i)
for (size_t j = c1; j <= c2; ++j)
result(i - r1, j - c1) = (*this)(i, j);
return result;
}
// Full row
ICoreArray<T> row(const size_t r) const {
return slice(r, r, 0, cols - 1);
}
// Full column
ICoreArray<T> col(const size_t c) const {
return slice(0, rows - 1, c, c);
}
ICoreArray<T> operator|(const ICoreArray<T>& other) const {
size_t newRows = std::max(rows, other.rows);
size_t newCols = cols + other.cols;
ICoreArray<T> result(newRows, newCols);
// copy left
for (size_t i = 0; i < rows; ++i)
for (size_t j = 0; j < cols; ++j)
result(i,j) = (*this)(i,j);
// copy right
for (size_t i = 0; i < other.rows; ++i)
for (size_t j = 0; j < other.cols; ++j)
result(i, j + cols) = other(i,j);
return result;
}
ICoreArray<T> operator&(const ICoreArray<T>& other) const {
size_t newRows = rows + other.rows;
size_t newCols = std::max(cols, other.cols);
ICoreArray<T> result(newRows, newCols);
// copy top
for (size_t i = 0; i < rows; ++i)
for (size_t j = 0; j < cols; ++j)
result(i,j) = (*this)(i,j);
// copy bottom
for (size_t i = 0; i < other.rows; ++i)
for (size_t j = 0; j < other.cols; ++j)
result(i + rows, j) = other(i,j);
return result;
}
size_t getRows() const { return rows; }
size_t getCols() const { return cols; }
};
};
ICoreHashMap.h#
ICoreEssentials/Containers/ICoreHashMap.h
ICoreHashMap -- the project's own unordered key/value map.
PHASE 1 (current): a thin value wrapper around QHash<K, V>. Every operation forwards. Phase 3 replaces the member with std::unordered_map<K, V>.
The conversion shape, the class-not-alias decision and the "Qt's operators are templates, so deduction ignores our conversions" argument are all derived at the top of ICoreList.h -- read that file first; this one only records what is different.
⚠ THIS WRAPPER FRONTS QHash ONLY. QMap IS NOT PART OF IT.
The README's wave-8 line and the plan's B24 row both said "ICoreHashMap -- wraps QHash, QMap", and B24 found that to be wrong in a way that would not
Declares no class of its own — see the file.
ICoreList.h#
ICoreEssentials/Containers/ICoreList.h
ICoreList -- the project's own sequence container.
PHASE 1 (current): a thin value wrapper around QList<T>. Every operation forwards. Phase 3 replaces the member with std::vector<T>; there is nothing in the API below that std::vector cannot carry, which is the point of keeping the surface small.
DESIGN NOTES -- read before changing anything, none of these was free.
- IT IS A CLASS TEMPLATE, NOT AN ALIAS -- and the placeholder this file
replaced said the opposite ("probably an alias rather than a class, QList is already std::vector-shaped in Qt 6"). That suggestion was measured and rejected, because
template <class T> using ICoreList = QList<T>is not a wrapper at all: it is the SAME type under a second
Declares no class of its own — see the file.
ICoreMapIterator.h#
ICoreEssentials/Containers/ICoreMapIterator.h
ICoreMapIterator -- the associative-container iterator this tree's call sites already spell. Internal to Containers/; not public API.
⚠ WHY THIS FILE EXISTS AT ALL, AND IT IS THE ONE THING W9.2 COULD NOT DO BY SWAPPING A MEMBER.
ICoreHashMapandICoreSortedMapused to typedef their iterator straight through toQHash's andQMap's, and those iterators havekey()andvalue()MEMBERS. The standard's associative iterators do not -- they dereference to astd::pairand you reach the halves through->firstand->second. Sixty-odd call sites in the SDK above this module -- the recipe interpreter, the Simulink codecs and the command engine -- are written asit.value(), so swapping the store tostd::unordered_mapwithout this adaptor would have been a sixty-file rewrite of working code to say the same thing in a different dialect.
ICoreMapIterator#
ICoreMapIterator.h:46 · class · 1 declaration(s)
class ICoreMapIterator {
public:
using inner_type = Inner;
using iterator_category = typename std::iterator_traits<Inner>::iterator_category;
using value_type = typename std::iterator_traits<Inner>::value_type;
using difference_type = typename std::iterator_traits<Inner>::difference_type;
// ⚠ NOT the inner iterator's pointer/reference: those are the pair's, and
// this iterator's are the VALUE's. See the header note.
//
// ⚠⚠ THE DOUBLE PARENTHESES ARE LOAD-BEARING AND THEIR ABSENCE COMPILES.
// `decltype(e->second)` on an UNPARENTHESIZED member access yields the
// member's DECLARED type -- `V` -- discarding both the reference and, on a
// const_iterator, the const. `decltype((e->second))` yields the type of
// the expression, `const V&`. Written the first way, `pointer` came out as
// `V*` on a const iterator and `operator->` failed to compile with
// "invalid conversion from 'const Entry*'", pointing at this header from
// three unrelated call sites.
using mapped_reference = decltype((std::declval<Inner&>()->second));
using pointer = typename std::remove_reference<mapped_reference>::type*;
using reference = mapped_reference;
ICoreMapIterator() = default;
ICoreMapIterator(Inner inner) : m_it(std::move(inner)) {}
// Converting from mutable to const, which is what makes `const_iterator it
// = map.find(k)` work on a non-const map -- the shape QHash allowed.
template <class Other,
class = typename std::enable_if<std::is_convertible<Other, Inner>::value>::type>
ICoreMapIterator(const ICoreMapIterator<Other>& other) : m_it(other.inner()) {}
const auto& key() const { return m_it->first; }
reference value() const { return m_it->second; }
// The VALUE, not the pair -- see the header note. `*it` and `it->field`
// both reach the mapped object.
reference operator*() const { return m_it->second; }
pointer operator->() const { return &m_it->second; }
ICoreMapIterator& operator++() { ++m_it; return *this; }
ICoreMapIterator operator++(int) { ICoreMapIterator copy = *this; ++m_it; return copy; }
// Only the ordered containers have these; instantiated on demand, so an
// unordered store never sees them.
ICoreMapIterator& operator--() { --m_it; return *this; }
ICoreMapIterator operator--(int) { ICoreMapIterator copy = *this; --m_it; return copy; }
const Inner& inner() const noexcept { return m_it; }
};
ICoreSet.h#
ICoreEssentials/Containers/ICoreSet.h
ICoreSet -- the project's own unordered unique-element container.
PHASE 1 (current): a thin value wrapper around QSet<T>. Every operation forwards. Phase 3 replaces the member with std::unordered_set<T>.
The conversion shape and the class-not-alias decision are derived at the top of ICoreList.h; this header only records what is different.
WHAT THIS TYPE IS ACTUALLY FOR, measured rather than assumed: 38 of the project's 40 QSet occurrences are QSet<ICoreString>, and every one of them is a seen-set -- handles already emitted, names already taken, warnings already issued. The API below is sized for that: insert, contains, iterate, and the initialiser-list form the SimulinkBridge keyword tables use. Set ALGEBRA (unite/intersect/subtract) is forwarded because QSet has it, but no
Declares no class of its own — see the file.
ICoreSortedMap.h#
ICoreEssentials/Containers/ICoreSortedMap.h
ICoreSortedMap -- the project's own ORDERED key/value map.
PHASE 1 (current): a thin value wrapper around QMap<K, V>. Phase 3 replaces the member with std::map<K, V>, which has the same ordering guarantee.
WHY THIS TYPE EXISTS AT ALL -- it is not in the plan, and B24 added it.
The plan's B24 row and the README's wave-8 line both scheduled QMap to go behind ICoreHashMap alongside QHash. That is a silent behaviour change: QMap iterates in ascending key order, QHash's order is unspecified and salt-randomised per process. Phase 1 changes no behaviour, so they cannot share a wrapper.
And it would have mattered. QMap has exactly ONE call site --
Declares no class of its own — see the file.
ICoreVariant.h#
ICoreEssentials/Containers/ICoreVariant.h
ICoreVariant#
ICoreVariant.h:64 · class · 31 declaration(s)
ICoreVariant -- a value of one of a handful of types, and the conversions between them.
class ICoreVariant {
public:
// ⚠ THE STORAGE IS AN OPAQUE BUFFER (H1.7, 2026-08-14): the tag, the three
// scalars and the ICoreString, held by value. A heap Impl would put a
// malloc on every settings read -- ICoreSettings::value() returns one BY
// VALUE and every `settings.value(k).toBool()` in ICoreUserPreferences
// builds one -- so this takes the small-value residue like the rest of the
// tier. Unlike ICorePoint and ICoreRect the state is NOT trivially
// copyable, because it holds an ICoreString, so all six special members are
// written out in the .cpp.
//
// The size is pinned by a static_assert against the real state, and the
// .cpp's comment records what it measured. Do not adjust it by arithmetic.
//
// *** 48 -> 64 ON 2026-08-24 (W9.1), AND THAT ROW DID NOT CHOOSE IT. ***
// The state holds an ICoreString BY VALUE, so this is the one pin in the
// tier that CASCADES: ICoreString's buffer went 24 -> 40 when its
// std::u16string store turned out not to be 24 outside libc++, and this
// state grew by exactly that. It stays an EXACT `==` pin -- the rule that
// made the three Text buffers upper bounds is about STANDARD-LIBRARY stores,
// and this state is our own struct.
//
// *** STILL NOT ARITHMETIC. *** Measured with a probe that had to reproduce
// today's 48 from a 24-byte string buffer before it was believed about 40's
// answer; the static_assert in the .cpp is what confirms it against the real
// state. docs/source/architecture/opaque-buffers.md.
static constexpr std::size_t kNativeStorageSize = 64;
static constexpr std::size_t kNativeStorageAlign = 8;
ICoreVariant();
ICoreVariant(const ICoreVariant&);
ICoreVariant(ICoreVariant&&) noexcept;
ICoreVariant& operator=(const ICoreVariant&);
ICoreVariant& operator=(ICoreVariant&&) noexcept;
~ICoreVariant();
ICoreVariant(bool v);
ICoreVariant(int v);
ICoreVariant(unsigned v);
ICoreVariant(long v);
ICoreVariant(unsigned long v);
ICoreVariant(long long v);
ICoreVariant(unsigned long long v);
ICoreVariant(double v);
ICoreVariant(float v);
ICoreVariant(const ICoreString& v);
ICoreVariant(const char* v);
// --- extraction ---------------------------------------------------------
// The coercion table at the top of this file is the contract; the bodies
// that implement it are in ICoreVariant.cpp and every rule is commented
// there, beside the code rather than away from it.
[[nodiscard]] bool toBool() const;
[[nodiscard]] int toInt(bool* ok = nullptr) const;
[[nodiscard]] unsigned toUInt(bool* ok = nullptr) const;
[[nodiscard]] std::int64_t toLongLong(bool* ok = nullptr) const;
[[nodiscard]] std::uint64_t toULongLong(bool* ok = nullptr) const;
[[nodiscard]] double toDouble(bool* ok = nullptr) const;
[[nodiscard]] float toFloat(bool* ok = nullptr) const;
[[nodiscard]] ICoreString toString() const;
[[nodiscard]] bool isValid() const noexcept;
[[nodiscard]] bool isNull() const noexcept;
void clear();
void swap(ICoreVariant& other) noexcept;
bool operator==(const ICoreVariant& other) const;
bool operator!=(const ICoreVariant& other) const;
};
ICoreHashMapNative.h#
ICoreEssentials/Containers/Backends/Native/ICoreHashMapNative.h
ICoreHashMap's non-Qt store: std::unordered_map<K, V>.
Selected when the library is built with ICORE_CORE_BACKEND_NATIVE. It publishes the same members as the QHash-backed store, minus the conversions to and from QHash. Include Containers/ICoreHashMap.h, never this file.
ICoreHashMap#
ICoreHashMapNative.h:52 · class · 18 declaration(s)
class ICoreHashMap {
public:
using key_type = K;
using mapped_type = V;
// ⚠ THE ADAPTER, NOT THE STORE'S OWN -- `*it` must be the VALUE, because
// ~40 constFind call sites in src/ICoreSDK read it that way. See
// ICoreMapIteratorNative.h, which exists for this one sentence.
using iterator = icore_containers_native::MapIterator<typename Store::iterator, K, V>;
using const_iterator = icore_containers_native::MapIterator<typename Store::const_iterator, K, const V>;
using size_type = std::ptrdiff_t;
// --- construction ------------------------------------------------------
ICoreHashMap() = default;
ICoreHashMap(const ICoreHashMap&) = default;
ICoreHashMap(ICoreHashMap&&) noexcept = default;
ICoreHashMap& operator=(const ICoreHashMap&) = default;
ICoreHashMap& operator=(ICoreHashMap&&) noexcept = default;
~ICoreHashMap() = default;
ICoreHashMap(std::initializer_list<std::pair<K, V>> items) {
for (const auto& item : items) { m_h.insert_or_assign(item.first, item.second); }
}
template <class It, class = typename std::iterator_traits<It>::iterator_category>
ICoreHashMap(It first_, It last_) : m_h(first_, last_) {}
// --- size and state ----------------------------------------------------
bool isEmpty() const noexcept { return m_h.empty(); }
bool empty() const noexcept { return m_h.empty(); }
size_type size() const noexcept { return static_cast<size_type>(m_h.size()); }
size_type count() const noexcept { return static_cast<size_type>(m_h.size()); }
void clear() { m_h.clear(); }
void reserve(size_type n) { if (n > 0) { m_h.reserve(static_cast<typename Store::size_type>(n)); } }
// QHash::squeeze() releases the spare buckets a COW hash holds; the std
// container has no equivalent request and needs none. QHash::detach()
// breaks sharing that does not exist here. Both kept so call sites compile,
// both no-ops, both saying so rather than pretending to act.
void squeeze() {}
void detach() {}
// --- lookup ------------------------------------------------------------
bool contains(const K& key) const { return m_h.find(key) != m_h.end(); }
// ⚠ A MISSING KEY IS A DEFAULT-CONSTRUCTED V, NEVER an insert and never UB.
// std::unordered_map::operator[] INSERTS on a miss; QHash::value() does
// not. Forwarding value() to operator[] would grow the map from a read, and
// it would do it silently.
V value(const K& key) const {
const auto it = m_h.find(key);
return it == m_h.end() ? V() : it->second;
}
V value(const K& key, const V& fallback) const {
const auto it = m_h.find(key);
return it == m_h.end() ? fallback : it->second;
}
V& operator[](const K& key) { return m_h[key]; }
// ⚠ CONST operator[] IS A READ, and QHash's returns a value rather than a
// reference precisely so it cannot insert. Same here.
V operator[](const K& key) const { return value(key); }
// Reverse lookup: the first key mapping to `value`, or a default K. O(n)
// on QHash too -- this is not a regression, it is the same linear walk.
K key(const V& value) const { return key(value, K()); }
K key(const V& value, const K& fallback) const {
for (const auto& entry : m_h) {
if (entry.second == value) { return entry.first; }
}
return fallback;
}
size_type count(const K& key) const { return contains(key) ? 1 : 0; }
iterator find(const K& key) { return iterator(m_h.find(key)); }
const_iterator find(const K& key) const { return const_iterator(m_h.find(key)); }
const_iterator constFind(const K& key) const { return const_iterator(m_h.find(key)); }
// keys() and values() hand back the WRAPPER, for the reason the Qt body
// gives: otherwise every hash.keys() puts a foreign container type back
// into a migrated file.
ICoreList<K> keys() const {
ICoreList<K> out;
out.reserve(size());
for (const auto& entry : m_h) { out.append(entry.first); }
return out;
}
ICoreList<K> keys(const V& value) const {
ICoreList<K> out;
for (const auto& entry : m_h) {
if (entry.second == value) { out.append(entry.first); }
}
return out;
}
ICoreList<V> values() const {
ICoreList<V> out;
out.reserve(size());
for (const auto& entry : m_h) { out.append(entry.second); }
return out;
}
// --- modification ------------------------------------------------------
// ⚠ OVERWRITES, because QHash::insert() does. std::unordered_map::insert()
// does NOT -- it keeps the existing value and reports failure, which would
// turn every "register this handle again" call site into a silent no-op.
// insert_or_assign is the one with QHash's semantics.
iterator insert(const K& key, const V& value) {
return iterator(m_h.insert_or_assign(key, value).first);
}
void insert(const ICoreHashMap& other) {
for (const auto& entry : other.m_h) { m_h.insert_or_assign(entry.first, entry.second); }
}
template <class... Args>
iterator emplace(const K& key, Args&&... args) {
return iterator(m_h.insert_or_assign(key, V(std::forward<Args>(args)...)).first);
}
size_type remove(const K& key) { return static_cast<size_type>(m_h.erase(key)); }
// ⚠ THE PREDICATE TAKES AN ITERATOR, which is QHash's primary form and the
// one this tree would write. Qt also accepts a std::pair form; no call site
// uses it, and accepting both here would need a constraint rather than an
// overload, so it is left out until something asks.
template <class Predicate>
size_type removeIf(Predicate pred) {
size_type removed = 0;
for (auto it = m_h.begin(); it != m_h.end();) {
if (pred(iterator(it))) { it = m_h.erase(it); ++removed; }
else { ++it; }
}
return removed;
}
V take(const K& key) {
const auto it = m_h.find(key);
if (it == m_h.end()) { return V(); }
V out = std::move(it->second);
m_h.erase(it);
return out;
}
iterator erase(const_iterator pos) { return iterator(m_h.erase(pos.store())); }
void swap(ICoreHashMap& other) noexcept { m_h.swap(other.m_h); }
// --- iteration ---------------------------------------------------------
iterator begin() { return iterator(m_h.begin()); }
iterator end() { return iterator(m_h.end()); }
const_iterator begin() const { return const_iterator(m_h.begin()); }
const_iterator end() const { return const_iterator(m_h.end()); }
const_iterator cbegin() const { return const_iterator(m_h.cbegin()); }
const_iterator cend() const { return const_iterator(m_h.cend()); }
const_iterator constBegin() const { return const_iterator(m_h.cbegin()); }
const_iterator constEnd() const { return const_iterator(m_h.cend()); }
};
ICoreListNative.h#
ICoreEssentials/Containers/Backends/Native/ICoreListNative.h
ICoreList's non-Qt store: std::vector<T>.
Selected when the library is built with ICORE_CORE_BACKEND_NATIVE. It publishes the same members as the QList-backed store, minus the conversions to and from QList, which have nothing to convert to on a build with no Qt in it. Include Containers/ICoreList.h, never this file.
ICoreList#
ICoreListNative.h:72 · class · 25 declaration(s)
class ICoreList {
public:
using value_type = T;
using iterator = typename Store::iterator;
using const_iterator = typename Store::const_iterator;
// ⚠ FROM THE STORE, NOT `T&` -- see the vector<bool> note in the banner.
using reference = typename Store::reference;
using const_reference = typename Store::const_reference;
// ⚠ SIGNED, and the same width as the qsizetype it replaces: every index
// parameter and every returned count below is this type, so a call site
// comparing a size against a signed int keeps compiling and keeps meaning
// the same thing. std::vector's own size_type is UNSIGNED, which is why
// every forwarder here casts rather than returning the store's number.
using size_type = std::ptrdiff_t;
// --- construction ------------------------------------------------------
ICoreList() = default;
ICoreList(const ICoreList&) = default;
ICoreList(ICoreList&&) noexcept = default;
ICoreList& operator=(const ICoreList&) = default;
ICoreList& operator=(ICoreList&&) noexcept = default;
~ICoreList() = default;
ICoreList(std::initializer_list<T> items) : m_v(items) {}
explicit ICoreList(size_type n) : m_v(toStore(n)) {}
ICoreList(size_type n, const T& value) : m_v(toStore(n), value) {}
// CONSTRAINED for H12's reason, which is a hazard the WRAPPER introduces
// and is documented in full in ICoreList.h. The constraint is not optional
// and is not a tidiness point.
template <class It, class = typename std::iterator_traits<It>::iterator_category>
ICoreList(It first_, It last_) : m_v(first_, last_) {}
// --- size and state ----------------------------------------------------
bool isEmpty() const noexcept { return m_v.empty(); }
bool empty() const noexcept { return m_v.empty(); }
size_type size() const noexcept { return fromStore(m_v.size()); }
size_type count() const noexcept { return fromStore(m_v.size()); }
size_type length() const noexcept { return fromStore(m_v.size()); }
void clear() { m_v.clear(); }
void reserve(size_type n) { if (n > 0) { m_v.reserve(toStore(n)); } }
void resize(size_type n) { m_v.resize(toStore(n)); }
void resize(size_type n, const T& value) { m_v.resize(toStore(n), value); }
// ⚠ QList::squeeze() releases the capacity a COW container is holding;
// shrink_to_fit is the same REQUEST on a std container and is equally
// non-binding. Kept rather than dropped so the 4 call sites compile.
void squeeze() { m_v.shrink_to_fit(); }
// ⚠ A NO-OP ON PURPOSE, NOT AN OMISSION. detach() exists because QList is
// copy-on-write and a caller about to hand out an iterator may need its own
// copy first. std::vector has no sharing to break, so the call has nothing
// to do -- and the 3 call sites are asking for a guarantee this store gives
// unconditionally.
void detach() {}
// --- element access ----------------------------------------------------
const_reference at(size_type i) const { return m_v[toStore(i)]; }
reference operator[](size_type i) { return m_v[toStore(i)]; }
const_reference operator[](size_type i) const { return m_v[toStore(i)]; }
reference first() { return m_v.front(); }
const_reference first() const { return m_v.front(); }
reference last() { return m_v.back(); }
const_reference last() const { return m_v.back(); }
const_reference constFirst() const { return m_v.front(); }
const_reference constLast() const { return m_v.back(); }
reference front() { return m_v.front(); }
const_reference front() const { return m_v.front(); }
reference back() { return m_v.back(); }
const_reference back() const { return m_v.back(); }
// ⚠ OUT OF RANGE IS A DEFAULT-CONSTRUCTED T, NOT UB. This is the whole
// reason value() exists beside at(), and a std::vector spelling that
// indexes blind would turn a defined answer into a crash months later.
T value(size_type i) const {
return (i < 0 || i >= size()) ? T() : m_v[toStore(i)];
}
T value(size_type i, const T& fallback) const {
return (i < 0 || i >= size()) ? fallback : m_v[toStore(i)];
}
T* data() { return m_v.data(); }
const T* data() const { return m_v.data(); }
const T* constData() const { return m_v.data(); }
// --- iteration ---------------------------------------------------------
iterator begin() { return m_v.begin(); }
iterator end() { return m_v.end(); }
const_iterator begin() const { return m_v.begin(); }
const_iterator end() const { return m_v.end(); }
const_iterator cbegin() const { return m_v.cbegin(); }
const_iterator cend() const { return m_v.cend(); }
const_iterator constBegin() const { return m_v.cbegin(); }
const_iterator constEnd() const { return m_v.cend(); }
auto rbegin() { return m_v.rbegin(); }
auto rend() { return m_v.rend(); }
auto rbegin() const { return m_v.rbegin(); }
auto rend() const { return m_v.rend(); }
// --- adding and removing -----------------------------------------------
void append(const T& v) { m_v.push_back(v); }
void append(T&& v) { m_v.push_back(std::move(v)); }
void append(const ICoreList& other) {
m_v.insert(m_v.end(), other.m_v.begin(), other.m_v.end());
}
void prepend(const T& v) { m_v.insert(m_v.begin(), v); }
void prepend(T&& v) { m_v.insert(m_v.begin(), std::move(v)); }
void push_back(const T& v) { m_v.push_back(v); }
void push_back(T&& v) { m_v.push_back(std::move(v)); }
void push_front(const T& v) { m_v.insert(m_v.begin(), v); }
void pop_back() { m_v.pop_back(); }
void pop_front() { m_v.erase(m_v.begin()); }
template <class... Args>
reference emplaceBack(Args&&... args) { return m_v.emplace_back(std::forward<Args>(args)...); }
template <class... Args>
reference emplace_back(Args&&... args) { return m_v.emplace_back(std::forward<Args>(args)...); }
void insert(size_type i, const T& v) { m_v.insert(m_v.begin() + toStore(i), v); }
iterator insert(const_iterator before, const T& v) { return m_v.insert(before, v); }
void removeAt(size_type i) { m_v.erase(m_v.begin() + toStore(i)); }
size_type removeAll(const T& v) {
const auto cut = std::remove(m_v.begin(), m_v.end(), v);
const size_type removed = static_cast<size_type>(std::distance(cut, m_v.end()));
m_v.erase(cut, m_v.end());
return removed;
}
bool removeOne(const T& v) {
const auto it = std::find(m_v.begin(), m_v.end(), v);
if (it == m_v.end()) { return false; }
m_v.erase(it);
return true;
}
void removeFirst() { m_v.erase(m_v.begin()); }
void removeLast() { m_v.pop_back(); }
template <class Predicate>
size_type removeIf(Predicate pred) {
const auto cut = std::remove_if(m_v.begin(), m_v.end(), pred);
const size_type removed = static_cast<size_type>(std::distance(cut, m_v.end()));
m_v.erase(cut, m_v.end());
return removed;
}
T takeAt(size_type i) {
T out = std::move(m_v[toStore(i)]);
m_v.erase(m_v.begin() + toStore(i));
return out;
}
T takeFirst() { T out = std::move(m_v.front()); m_v.erase(m_v.begin()); return out; }
T takeLast() { T out = std::move(m_v.back()); m_v.pop_back(); return out; }
iterator erase(const_iterator pos) { return m_v.erase(pos); }
iterator erase(const_iterator first_, const_iterator last_) { return m_v.erase(first_, last_); }
void swapItemsAt(size_type i, size_type j) {
using std::swap;
swap(m_v[toStore(i)], m_v[toStore(j)]);
}
// ⚠ A ROTATE, NOT A SWAP. QList::move(from, to) lifts one element out and
// re-inserts it at `to`, sliding everything between them along by one. A
// swap of the two positions is a different list whenever |from - to| > 1,
// and it is the mistake this member exists to not make.
void move(size_type from, size_type to) {
if (from == to) { return; }
const auto f = m_v.begin() + toStore(from);
const auto t = m_v.begin() + toStore(to);
if (from < to) { std::rotate(f, f + 1, t + 1); }
else { std::rotate(t, f, f + 1); }
}
// The SIZE is unchanged; every element becomes v. (QList::fill(v) with no
// second argument means exactly this.)
void fill(const T& v) { std::fill(m_v.begin(), m_v.end(), v); }
// --- searching ---------------------------------------------------------
bool contains(const T& v) const {
return std::find(m_v.begin(), m_v.end(), v) != m_v.end();
}
// ⚠ A NEGATIVE `from` COUNTS BACK FROM THE END -- QList's rule, and a
// std::find that started at begin() + from would index out of the store.
size_type indexOf(const T& v, size_type from = 0) const {
size_type start = from;
if (start < 0) { start = std::max<size_type>(size() + start, 0); }
if (start >= size()) { return -1; }
const auto it = std::find(m_v.begin() + toStore(start), m_v.end(), v);
return it == m_v.end() ? -1 : static_cast<size_type>(std::distance(m_v.begin(), it));
}
size_type lastIndexOf(const T& v, size_type from = -1) const {
size_type start = from;
if (start < 0) { start += size(); }
if (start >= size()) { start = size() - 1; }
for (size_type i = start; i >= 0; --i) {
if (m_v[toStore(i)] == v) { return i; }
}
return -1;
}
size_type count(const T& v) const {
return static_cast<size_type>(std::count(m_v.begin(), m_v.end(), v));
}
bool startsWith(const T& v) const { return !m_v.empty() && m_v.front() == v; }
bool endsWith(const T& v) const { return !m_v.empty() && m_v.back() == v; }
// --- slicing (returns the wrapper, so a chain never leaks the store) ----
// ⚠ mid() CLAMPS AND sliced() DOES NOT, which is QList's own split and not
// an inconsistency here: mid() is the forgiving one (a position past the
// end is an empty list, a short tail is the tail), sliced() has a
// precondition its callers are expected to have met.
ICoreList mid(size_type pos, size_type n = -1) const {
if (pos < 0) { pos = 0; }
if (pos >= size()) { return ICoreList(); }
size_type take = (n < 0 || pos + n > size()) ? size() - pos : n;
return ICoreList(m_v.begin() + toStore(pos), m_v.begin() + toStore(pos + take));
}
ICoreList sliced(size_type pos) const {
return ICoreList(m_v.begin() + toStore(pos), m_v.end());
}
ICoreList sliced(size_type pos, size_type n) const {
return ICoreList(m_v.begin() + toStore(pos), m_v.begin() + toStore(pos + n));
}
ICoreList first(size_type n) const {
return ICoreList(m_v.begin(), m_v.begin() + toStore(n));
}
ICoreList last(size_type n) const {
return ICoreList(m_v.end() - toStore(n), m_v.end());
}
// --- compound operators (members, so they join no foreign overload set) -
ICoreList& operator<<(const T& v) { m_v.push_back(v); return *this; }
ICoreList& operator<<(const ICoreList& other) { append(other); return *this; }
ICoreList& operator+=(const T& v) { m_v.push_back(v); return *this; }
ICoreList& operator+=(const ICoreList& other) { append(other); return *this; }
ICoreList operator+(const ICoreList& other) const {
ICoreList out(*this);
out.append(other);
return out;
}
void swap(ICoreList& other) noexcept { m_v.swap(other.m_v); }
};
ICoreMapIteratorNative.h#
ICoreEssentials/Containers/Backends/Native/ICoreMapIteratorNative.h
The iterator ICoreHashMap and ICoreSortedMap publish over a std:: map store.
Dereferencing it yields the mapped VALUE and key() yields the key, which is the contract the Qt-backed stores have always had. A std:: map iterator yields a std::pair instead, so this adapter exists to keep one contract across both stores. Not for direct use.
MapIterator#
ICoreMapIteratorNative.h:46 · class · 4 declaration(s)
class MapIterator {
public:
using iterator_category = std::forward_iterator_tag;
using value_type = Value;
using difference_type = std::ptrdiff_t;
using pointer = Value*;
using reference = Value&;
MapIterator() = default;
explicit MapIterator(StoreIt it) : m_it(it) {}
// ⚠ MUTABLE -> CONST, because the call sites MIX THEM and QHash lets them:
// `const auto it = map.find(k); it != map.constEnd()` is a live shape in
// this tree. The store's own iterators convert the same way, so this is
// the conversion being forwarded rather than one being invented. It is
// constrained to a MapIterator, so it joins no other overload set (H12).
template <class OtherIt, class OtherValue,
class = decltype(StoreIt(std::declval<OtherIt>()))>
MapIterator(const MapIterator<OtherIt, Key, OtherValue>& other)
: m_it(other.store()) {}
// ⚠ THE WHOLE POINT OF THE FILE: the VALUE, not the pair.
reference operator*() const { return m_it->second; }
pointer operator->() const { return &m_it->second; }
const Key& key() const { return m_it->first; }
reference value() const { return m_it->second; }
MapIterator& operator++() { ++m_it; return *this; }
MapIterator operator++(int) { MapIterator copy(*this); ++m_it; return copy; }
// Templated on the OTHER side's store iterator for the same mixing reason:
// a const and a mutable store iterator compare directly, and requiring both
// sides to be the same MapIterator would reject the shape above.
template <class OtherIt, class OtherValue>
bool operator==(const MapIterator<OtherIt, Key, OtherValue>& other) const {
return m_it == other.store();
}
template <class OtherIt, class OtherValue>
bool operator!=(const MapIterator<OtherIt, Key, OtherValue>& other) const {
return !(m_it == other.store());
}
// The store iterator, for the wrapper's own erase()/insert() plumbing. Not
// part of the published contract -- a call site that reaches for it is
// reaching past the wrapper.
StoreIt store() const { return m_it; }
};
ICoreSetNative.h#
ICoreEssentials/Containers/Backends/Native/ICoreSetNative.h
ICoreSet's non-Qt store: std::unordered_set<T>.
Selected when the library is built with ICORE_CORE_BACKEND_NATIVE. It publishes the same members as the QSet-backed store, minus the conversions to and from QSet. Include Containers/ICoreSet.h, never this file.
ICoreSet#
ICoreSetNative.h:42 · class · 13 declaration(s)
class ICoreSet {
public:
using value_type = T;
using iterator = typename Store::const_iterator;
using const_iterator = typename Store::const_iterator;
using size_type = std::ptrdiff_t;
// --- construction ------------------------------------------------------
ICoreSet() = default;
ICoreSet(const ICoreSet&) = default;
ICoreSet(ICoreSet&&) noexcept = default;
ICoreSet& operator=(const ICoreSet&) = default;
ICoreSet& operator=(ICoreSet&&) noexcept = default;
~ICoreSet() = default;
ICoreSet(std::initializer_list<T> items) : m_s(items) {}
// CONSTRAINED for H12's reason -- ICoreList.h states it in full, and
// ICoreSet is the second of the two wrappers that carried the unsafe shape.
template <class It, class = typename std::iterator_traits<It>::iterator_category>
ICoreSet(It first_, It last_) : m_s(first_, last_) {}
// --- size and state ----------------------------------------------------
bool isEmpty() const noexcept { return m_s.empty(); }
bool empty() const noexcept { return m_s.empty(); }
size_type size() const noexcept { return static_cast<size_type>(m_s.size()); }
size_type count() const noexcept { return static_cast<size_type>(m_s.size()); }
void clear() { m_s.clear(); }
void reserve(size_type n) { if (n > 0) { m_s.reserve(static_cast<typename Store::size_type>(n)); } }
// No sharing to break and no spare buckets to hand back -- see the same
// pair on ICoreHashMapNative.h.
void squeeze() {}
void detach() {}
// --- lookup ------------------------------------------------------------
bool contains(const T& v) const { return m_s.find(v) != m_s.end(); }
// ⚠ SUBSET, not "has an element equal to that set". QSet::contains(QSet)
// asks whether every element of `other` is in this one, and reading it the
// other way would invert the answer on every call.
bool contains(const ICoreSet& other) const {
for (const T& v : other.m_s) {
if (!contains(v)) { return false; }
}
return true;
}
iterator find(const T& v) const { return m_s.find(v); }
const_iterator constFind(const T& v) const { return m_s.find(v); }
ICoreList<T> values() const {
ICoreList<T> out;
out.reserve(size());
for (const T& v : m_s) { out.append(v); }
return out;
}
// --- modification ------------------------------------------------------
iterator insert(const T& v) { return m_s.insert(v).first; }
// ⚠ QSet::remove RETURNS WHETHER IT REMOVED ONE. erase() returns a count,
// which is 0 or 1 for a set -- the same answer, different type, and a
// caller writing `if (set.remove(x))` reads it either way. Spelled as the
// bool the surface promises rather than left to a silent conversion.
bool remove(const T& v) { return m_s.erase(v) > 0; }
template <class Predicate>
size_type removeIf(Predicate pred) {
size_type removed = 0;
for (auto it = m_s.begin(); it != m_s.end();) {
if (pred(*it)) { it = m_s.erase(it); ++removed; }
else { ++it; }
}
return removed;
}
iterator erase(const_iterator pos) { return m_s.erase(pos); }
void swap(ICoreSet& other) noexcept { m_s.swap(other.m_s); }
// --- set algebra -------------------------------------------------------
ICoreSet& unite(const ICoreSet& other) {
for (const T& v : other.m_s) { m_s.insert(v); }
return *this;
}
ICoreSet& intersect(const ICoreSet& other) {
for (auto it = m_s.begin(); it != m_s.end();) {
if (other.contains(*it)) { ++it; } else { it = m_s.erase(it); }
}
return *this;
}
ICoreSet& subtract(const ICoreSet& other) {
for (const T& v : other.m_s) { m_s.erase(v); }
return *this;
}
bool intersects(const ICoreSet& other) const {
// Walk the SMALLER side: an intersection test on two hashes is
// min(n, m) lookups, and QSet::intersects does the same.
const ICoreSet& small = (size() <= other.size()) ? *this : other;
const ICoreSet& large = (size() <= other.size()) ? other : *this;
for (const T& v : small.m_s) {
if (large.contains(v)) { return true; }
}
return false;
}
// --- iteration ---------------------------------------------------------
iterator begin() { return m_s.cbegin(); }
iterator end() { return m_s.cend(); }
const_iterator begin() const { return m_s.cbegin(); }
const_iterator end() const { return m_s.cend(); }
const_iterator cbegin() const { return m_s.cbegin(); }
const_iterator cend() const { return m_s.cend(); }
const_iterator constBegin() const { return m_s.cbegin(); }
const_iterator constEnd() const { return m_s.cend(); }
// --- compound operators (members, so they join no foreign overload set) -
ICoreSet& operator<<(const T& v) { m_s.insert(v); return *this; }
ICoreSet& operator+=(const ICoreSet& other) { return unite(other); }
ICoreSet& operator-=(const ICoreSet& other) { return subtract(other); }
ICoreSet& operator|=(const ICoreSet& other) { return unite(other); }
ICoreSet& operator&=(const ICoreSet& other) { return intersect(other); }
};
ICoreSortedMapNative.h#
ICoreEssentials/Containers/Backends/Native/ICoreSortedMapNative.h
ICoreSortedMap's non-Qt store: std::map<K, V>.
Selected when the library is built with ICORE_CORE_BACKEND_NATIVE. It publishes the same members as the QMap-backed store, minus the conversions to and from QMap. Both stores iterate in ascending key order, which is the property this container exists for. Include Containers/ICoreSortedMap.h.
ICoreSortedMap#
ICoreSortedMapNative.h:44 · class · 16 declaration(s)
class ICoreSortedMap {
public:
using key_type = K;
using mapped_type = V;
// The same adapter ICoreHashMap uses, for the same reason: `*it` is the
// VALUE on a QMap iterator and a std::pair on a std::map one.
using iterator = icore_containers_native::MapIterator<typename Store::iterator, K, V>;
using const_iterator = icore_containers_native::MapIterator<typename Store::const_iterator, K, const V>;
using size_type = std::ptrdiff_t;
// --- construction ------------------------------------------------------
ICoreSortedMap() = default;
ICoreSortedMap(const ICoreSortedMap&) = default;
ICoreSortedMap(ICoreSortedMap&&) noexcept = default;
ICoreSortedMap& operator=(const ICoreSortedMap&) = default;
ICoreSortedMap& operator=(ICoreSortedMap&&) noexcept = default;
~ICoreSortedMap() = default;
ICoreSortedMap(std::initializer_list<std::pair<K, V>> items) {
for (const auto& item : items) { m_m.insert_or_assign(item.first, item.second); }
}
template <class It, class = typename std::iterator_traits<It>::iterator_category>
ICoreSortedMap(It first_, It last_) : m_m(first_, last_) {}
// --- size and state ----------------------------------------------------
bool isEmpty() const noexcept { return m_m.empty(); }
bool empty() const noexcept { return m_m.empty(); }
size_type size() const noexcept { return static_cast<size_type>(m_m.size()); }
size_type count() const noexcept { return static_cast<size_type>(m_m.size()); }
void clear() { m_m.clear(); }
void detach() {}
// --- lookup ------------------------------------------------------------
bool contains(const K& key) const { return m_m.find(key) != m_m.end(); }
// A miss is a default-constructed V and never an insert -- QMap's rule.
// std::map::operator[] would insert, which is the trap this member exists
// to not fall into. Stated again here rather than cross-referenced: the two
// files are read separately and this is the one that bites.
V value(const K& key) const {
const auto it = m_m.find(key);
return it == m_m.end() ? V() : it->second;
}
V value(const K& key, const V& fallback) const {
const auto it = m_m.find(key);
return it == m_m.end() ? fallback : it->second;
}
V& operator[](const K& key) { return m_m[key]; }
V operator[](const K& key) const { return value(key); }
// ⚠ THE FIRST KEY IN ORDER whose value matches, because this store IS
// ordered -- QMap::key() walks the same way and gives the same answer, and
// a caller relying on it is relying on something real here rather than on
// an accident of bucket layout.
K key(const V& value) const { return key(value, K()); }
K key(const V& value, const K& fallback) const {
for (const auto& entry : m_m) {
if (entry.second == value) { return entry.first; }
}
return fallback;
}
size_type count(const K& key) const { return contains(key) ? 1 : 0; }
iterator find(const K& key) { return iterator(m_m.find(key)); }
const_iterator find(const K& key) const { return const_iterator(m_m.find(key)); }
const_iterator constFind(const K& key) const { return const_iterator(m_m.find(key)); }
// In ascending key order on both stores, which is the whole reason a caller
// reached for this container rather than ICoreHashMap.
ICoreList<K> keys() const {
ICoreList<K> out;
out.reserve(size());
for (const auto& entry : m_m) { out.append(entry.first); }
return out;
}
ICoreList<V> values() const {
ICoreList<V> out;
out.reserve(size());
for (const auto& entry : m_m) { out.append(entry.second); }
return out;
}
K firstKey() const { return m_m.begin()->first; }
K lastKey() const { return m_m.rbegin()->first; }
// --- modification ------------------------------------------------------
// Overwrites, because QMap::insert() does -- see ICoreHashMapNative.h's
// note on insert_or_assign, which is the same trap in the same tier.
iterator insert(const K& key, const V& value) {
return iterator(m_m.insert_or_assign(key, value).first);
}
void insert(const ICoreSortedMap& other) {
for (const auto& entry : other.m_m) { m_m.insert_or_assign(entry.first, entry.second); }
}
size_type remove(const K& key) { return static_cast<size_type>(m_m.erase(key)); }
template <class Predicate>
size_type removeIf(Predicate pred) {
size_type removed = 0;
for (auto it = m_m.begin(); it != m_m.end();) {
if (pred(iterator(it))) { it = m_m.erase(it); ++removed; }
else { ++it; }
}
return removed;
}
V take(const K& key) {
const auto it = m_m.find(key);
if (it == m_m.end()) { return V(); }
V out = std::move(it->second);
m_m.erase(it);
return out;
}
iterator erase(const_iterator pos) { return iterator(m_m.erase(pos.store())); }
void swap(ICoreSortedMap& other) noexcept { m_m.swap(other.m_m); }
// --- iteration ---------------------------------------------------------
iterator begin() { return iterator(m_m.begin()); }
iterator end() { return iterator(m_m.end()); }
const_iterator begin() const { return const_iterator(m_m.begin()); }
const_iterator end() const { return const_iterator(m_m.end()); }
const_iterator cbegin() const { return const_iterator(m_m.cbegin()); }
const_iterator cend() const { return const_iterator(m_m.cend()); }
const_iterator constBegin() const { return const_iterator(m_m.cbegin()); }
const_iterator constEnd() const { return const_iterator(m_m.cend()); }
};