Безопасный итератор для map и приватность POS указателей

  !   Данная информация предназначена только только для IT-специалистов по системной интеграции модулей БИОСОФТ-М. (см. Руководства пользователя к программным продуктам)

Нельзя продолжать iter-ирование map<> после удаления из него элементов.

Нельзя использовать iter от одного map для итерации другого.

Теперь для ПРД приложений делается автоматическая верификация этих правил во избежании фатальной коррупции памяти. При нарушении правил выдается ассерт. Игнорировать его нельзя, последствия этого будут неопределены (хотя и не фатальны в релизе).

Откуда проблема

В iter хранится POS которая по сути голый указатель на следующую пару key-->value в map.

Если эту пару удалить (RemoveAtKey, RemoveAll) то на следующем круге цикла пойдет коррупция.

Защита от фатала

С итератором и текущим состоянием map-контейнера ассоциированы уникальные сигнаруры.

Сигнатура заносится в iter при первом вызове Iterate (IterateKeys).Сигнатура в map обнрвляется при удалении элементов.На последующих кругах итерации сверяются сигнатуры iter и map. При несовпадении FAIL/return false

Сигнатуры глобально уникальны между всеми модулями процесса в рамках которых они получаются инкрементом в специальной разделяемой памяти.

Как поступать в приложении
Не применять map когда не нужно

Где array и Find() достаточны.

В идеале: избегать итераторов по map требующей удалений

Проблемы вообще нет если к map только добавляются элементы. Тогда можно публиковать Iterate по ней. Если же удаление необходимо то Iterate может работать по array который автоматически синхронизируется с map (map используется как индекс в array). Однако все равно при удалении из массива будут вопросы о том что станет с индексом в итераторе, но это уже офтоп.

Прерывать итератор после каждого удаления

Для локальных циклов где удаление не разнесено далеко от итератора можно рестартовать обработку элементов

    for_i(iRepeat, nCount)
    {
        bool bRestart = false;
        for (map.Iterate())
        {
            ...
            map.Remove...
            bRestart = true;
            break;
        }
 
        if (!bRestart)
        {
            break;
        }
    }
Делать копию map для итерации

Копию делать либо в array (эффективней) или в другой map. И итерировать копию. спокойно удаляя из основного map.

Копия в контексте iter

Один из вариантов решения для глубоко инкапсулированных Iterate() интерфейсов - сохранить копию map, или array ключей в Qmap хранит расширенный контекст итератора по которой итерировать независимо от меняющегося исходного map.

Отложенное удаление

Соствлять список того, что надо удалить или ставить элементам флаг "удалено". Применять физическое удаление к map в отложенном режиме если этого допускает логика (по таймеру и т.п.). Будет работать если клиент не хранит iter а использует только локально (что всегда настоятельно рекомендуется во избежении кашмарных проблем всюду).

Универсальный безопасный итератор

Давно думаю о таком хотя бы как опциональной альтернативе. Он будет заведомо более медленным чем не толерантный к удалению.

Возможный вариант реализации: хранение в iter копии уже проитерированных ключей которые используются для автоматического Iterate-recovery and resume when deletion during iteration is detected.

Другие решения, в частности временная блокировка следующего итерируемого элемента (собственно источника всей проблемы) от удаления слишком усложняют и замедляют всю работу map<>.

links

нигде эффективного встроенного решения не вижу: