Generated reference › API — ICoreEssentials/Containers
kind: generated#api#icoreessentials-containers

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. ICoreHashMap and ICoreSortedMap used to typedef their iterator straight through to QHash's and QMap's, and those iterators have key() and value() MEMBERS. The standard's associative iterators do not -- they dereference to a std::pair and you reach the halves through ->first and ->second. Sixty-odd call sites in the SDK above this module -- the recipe interpreter, the Simulink codecs and the command engine -- are written as it.value(), so swapping the store to std::unordered_map without 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()); }

};