Re[17]: Исповедь C++ника
От: so5team https://stiffstream.com
Дата: 28.12.20 06:26
Оценка: 5 (1)
Здравствуйте, Lexey, Вы писали:

BFE>>Только вот при вызове Button::RemoveOnButtonPress() внутри callback'а скорее всего получится segmentation fault или другое UB: ведь в момент вызова callback'а внутри прохода std::for_each будет удалён объект на который указывает текущий итератор.


L>Это легко обходится через копирование колбэков во временный контейнер и вызов for_each поверх него. Это довольно стандартное решение для ситуаций, когда возможны описки из колбэков.


С простым копированием не так все просто: RemoveOnButtonPress может вызываться не только для удаления текущего обработчика, но и для любого другого. В том числе и для тех обработчиков, до которых внутри for_each-а пока не дошли. Если for_each будет идти по временному контейнеру, то получится, что во временном контейнере останется обработчик, который уже изъят из основного. И будет вызван. Что может сильно удивить пользователя, который этот обработчик только что изъял.

Тут нужна более хитрая схема. Например, с дополнительным флагом valid/invalid для каждой подписки. Если подписка удаляется в момент публикации (т.е. внутри for_each-а), то физически она не удаляется, а флаг для нее меняется на invalid (+ еще взводится отдельный флаг, который показывает, что список подписок изменился). Перед вызовом подписчика проверяется флаг для него. Если valid, то вызывается. Если invalid, то пропускается. После выхода из for_each-а публикации проверяется общий флаг наличия изменений в списке. Если изменения есть, то идет еще один цикл с изъятием всех invalid подписок.

А если добавить сюда еще и возможность вызова AddOnButtonPress в момент публикации, то ситуация становится еще интереснее. И, возможно, использование std::list вместо std::vector в таких условиях более оправдано, не смотря на все недостатки std::list-а.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.