Russian Qt Forum

Qt => Общие вопросы => Тема начата: Igors от Февраль 14, 2020, 13:56



Название: Qt + вумные указватели
Отправлено: Igors от Февраль 14, 2020, 13:56
Добрый день

Предлагаю выделить ветку обсуждения в новую тему.

А если бы всё-таки решили в Qt использовать умные указатели и контейнеры std, то как бы хранили список детей (children) для QObject?

Например, так (https://codereview.qt-project.org/c/qt/qtbase/+/231543)

Для такой огромной кодовой базы родом из 90-х, умные указатели действительно мало что дадут. Полагаю, Qt до скончания своих веков будет на голых указателях жить. Если менять на умные нормально, там много чего переделывать придётся, и это будет уже совсем другой Qt. Но это не значит, что надо опустить руки и ничего не делать :).

Лично я, мягко говоря, не уверен что это вообще стоит делать. Ну вот напр QTreeWidgetItem. Им вроде бы "владеет" QTreeWidgetView, но всякий желающий может его спокойно грохнуть (delete item). А можно айтем и "забрать" (take). Пусть это не шедевр инженерной мысли, но такая организация данных мне кажется вполне разумной и каких-то претензий к ней у меня нет.

Что мы хотим улучшить? Не понимаю. Затевать здесь гемор с юниками мне кажется абсолютно ни к чему. Думается срабатывает "фетиш" типа "голый/raw = плохо", никаких др оснований не видно.

Ваше мнение?


Название: Re: Qt + вумные указватели
Отправлено: ViTech от Февраль 14, 2020, 14:10
Затевать здесь гемор с юниками мне кажется абсолютно ни к чему.

Юники нужны, чтобы не было тем подобных этой: Непонятное удаление cell widget (http://www.prog.org.ru/topic_32593_0.html).

Собственно в этих двух методах:
Код
C++ (Qt)
template<class T>
void adoptChild(T *child);
 
template<class T>
T* adoptChild(std::unique_ptr<T> child);
меня и смущает, что по голому указателю передаётся владение. Хотя в других случаях с голым указателем - нет, например, QLabel::setBuddy(QWidget *buddy). Может это исключение из правил, но в глаза бросилось.


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 14, 2020, 14:57
Юники нужны, чтобы не было тем подобных этой: Непонятное удаление cell widget (http://www.prog.org.ru/topic_32593_0.html).
Так это вроде "наоборот", жалуется на отсутствие ф-ционала "take". Уникальность владения лишь усугубит такие проблемы.

Ну хорошо, допустим (ценой немалых усилий) мы все же добились что айтемы стали юниками. Что это дает? Теперь мы уж точно знаем кто ими владеет!! (а так не знали :)) Разве невозможны данные которые многие (или всякий) может удалить? Что в этом плохого и почему с этим надо бороться?  :) 


Название: Re: Qt + вумные указватели
Отправлено: ViTech от Февраль 14, 2020, 15:35
Так это вроде "наоборот", жалуется на отсутствие ф-ционала "take". Уникальность владения лишь усугубит такие проблемы.

Так сначала-то хотелось "оставить" объект себе, а юник такое явно запретил бы, и вопроса: "Почему это происходит?" не возникло. Сразу возник бы второй вопрос: "назад не отобрать уже никак?", но это уже другая история :).


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 14, 2020, 16:05
меня и смущает, что по голому указателю передаётся владение. Хотя в других случаях с голым указателем - нет, например, QLabel::setBuddy(QWidget *buddy). Может это исключение из правил, но в глаза бросилось.

А это не смущает?
Код:
new MyObject(this);

Особенно весело когда идешь в код MyObject, а там
Код:
explicit MyObject(MyObjectManager *manager, QObject *parent = nullptr);

Я уже объяснил в соседнем топике, что adoptChild от указателя нужен в переходный период потому что позволяет посадить обезьянку и сделать s/child->setParent(parent)/parent->adoptChild(child)/g не трогая при этом типы указателей.


Название: Re: Qt + вумные указватели
Отправлено: ViTech от Февраль 14, 2020, 16:52
А это не смущает?
Код:
new MyObject(this);

Особенно весело когда идешь в код MyObject, а там
Код:
explicit MyObject(MyObjectManager *manager, QObject *parent = nullptr);

В текущей версии меня столько всего смущает, что об этом и говорить не стоит :).

Я уже объяснил в соседнем топике, что adoptChild от указателя нужен в переходный период потому что позволяет посадить обезьянку и сделать s/child->setParent(parent)/parent->adoptChild(child)/g не трогая при этом типы указателей.

Раз надо так надо, что тут сказать :).


Название: Re: Qt + вумные указватели
Отправлено: ViTech от Февраль 17, 2020, 11:19
Ну вот напр QTreeWidgetItem. Им вроде бы "владеет" QTreeWidgetView, но всякий желающий может его спокойно грохнуть (delete item). А можно айтем и "забрать" (take). Пусть это не шедевр инженерной мысли, но такая организация данных мне кажется вполне разумной и каких-то претензий к ней у меня нет.

Сначала надо попросить владельца отдать объект (take/release/и т.п.), и если он отпустит его на свободу, тогда можно делать с этим объектом что угодно, в том числе удалять. А не так, что кто угодно, когда угодно, мог удалить любой объект.

Ну хорошо, допустим (ценой немалых усилий) мы все же добились что айтемы стали юниками. Что это дает? Теперь мы уж точно знаем кто ими владеет!! (а так не знали :)) Разве невозможны данные которые многие (или всякий) может удалить? Что в этом плохого и почему с этим надо бороться?  :)

Едите Вы на автомобиле, и внезапно "многий" удаляет двигатель, а "всякий" - тормоза :). Как Вам понравится такая поездка? Нужно ли с этим бороться?


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 17, 2020, 17:00
Сначала надо попросить владельца отдать объект (take/release/и т.п.), и если он отпустит его на свободу, тогда можно делать с этим объектом что угодно, в том числе удалять. А не так, что кто угодно, когда угодно, мог удалить любой объект.
Это разумные соображения "вообще", но не конкретно для айтема. Попробуйте написать такое удаление - получится полная фигня

Едите Вы на автомобиле, и внезапно "многий" удаляет двигатель, а "всякий" - тормоза :). Как Вам понравится такая поездка? Нужно ли с этим бороться?
Опять в общем случае - да, конкретно - нет. В данном случае работоспособность автомобиля (QTreeWidget) гарантируется, т.к. он отслеживает удаление. Заметим что такая простая "ассоциативная связь" никаким вумным указателем не выражается.

Мне кажется очевидным что "юник" и "шаред" - всего лишь 2 случая, пусть популярных, но пригодных далеко не всегда. Не нужно пытаться их впихнуть всегда и везде. Если для выражения данной сущности они не подходят (как в случае айтема), то или свой велик или голый указатель, ничего "такого уж плохого" в нем нет


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 17, 2020, 18:07
Это разумные соображения "вообще", но не конкретно для айтема. Попробуйте написать такое удаление - получится полная фигня

Пробовал (https://github.com/ABBAPOH/textureviewer/blob/master/src/libs/utils/treemodelitem.h). И уже дважды постил на форуме. Получается отлично.


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 17, 2020, 18:20
Опять в общем случае - да, конкретно - нет. В данном случае работоспособность автомобиля (QTreeWidget) гарантируется, т.к. он отслеживает удаление. Заметим что такая простая "ассоциативная связь" никаким вумным указателем не выражается.

TreeItem уникально владеет своими детьми. QTreeWidget уникально владеет rootItem. Что тут не выражается вумным указателем??


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 17, 2020, 19:05
Пробовал (https://github.com/ABBAPOH/textureviewer/blob/master/src/libs/utils/treemodelitem.h). И уже дважды постил на форуме. Получается отлично.
Не сказал бы. Ну может я чего-то не понял.
Код
C++ (Qt)
ItemObserverPointer item = ...;  //  как-то получили айтем
...
// теперь его надо удалить (напр юзер нажал Del)
// delete item;  // так нельзя
// а тогда как ???
Это надо брать парента, проверять его на null и звать remove, так, что ли? И разве после этого item потеряет валидность? Как-то коряво
TreeItem уникально владеет своими детьми. QTreeWidget уникально владеет rootItem.
Владеет - да, удаление QTreeWidget вызовет удаление всех айтемов. Но зачем навязывать уникальность если по смыслу ее нет? Напр диалог/окно (владелец QTreeWidget) что, не может удалять?
Что тут не выражается вумным указателем??
Вектор чайлдов, нужно "вычернуть" айтем из него - этого вумники не делают. И все равно приходится иметь remove для этого, а перенести его вызов в caller'a усложняет код но ровным счетом ничего не меняет.


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 17, 2020, 19:21
Это надо брать парента, проверять его на null и звать remove, так, что ли? И разве после этого item потеряет валидность? Как-то коряво
Парент есть по определению всегда. Если это не рут, который вы не можете удалить по определению=)

Либо у вас observer_ptr и вы айтемом не владеете (а значит и удалить не можете).
Либо у вас unique_ptr и вы c айтемом можете делать что хотите.

Да, в моем коде нет метода "достать" айтем, но его очень легко написать и пока не было нужно.

Владеет - да, удаление QTreeWidget вызовет удаление всех айтемов. Но зачем навязывать уникальность если по смыслу ее нет? Напр диалог/окно (владелец QTreeWidget) что, не может удалять?

Как нет уникальности, когда она есть? Если айтем в модели\вьюхе, то модель\вьюха уникально этим айтемом владеет.
Если айтем не в модели\вьюхе, то делайте с ним что хотите, хоть голым указателем пользуйтесь, но извольте "передать владение" когда будете добавлять в модель\вьюъу.

Вектор чайлдов, нужно "вычернуть" айтем из него - этого вумники не делают. И все равно приходится иметь remove для этого, а перенести его вызов в caller'a усложняет код но ровным счетом ничего не меняет.

Вот вам TreeModelItem, вполне себе "вумник" который умеет "вычернивать" айтемы. Совсем уже программисты обленились, дерево написать ленятся, всё им на блюдечке подавай.


Название: Re: Qt + вумные указватели
Отправлено: ViTech от Февраль 17, 2020, 19:44
Похоже грядёт очередной заход на Деревянный айтем (http://www.prog.org.ru/topic_32302_0.html)  :D.


Название: Re: Qt + вумные указватели
Отправлено: ssoft от Февраль 18, 2020, 07:25
Похоже грядёт очередной заход на Деревянный айтем (http://www.prog.org.ru/topic_32302_0.html)  :D.

Как раза хотел о том же сказать). Вот же пример иерархии tree-example (https://gitlab.com/ssoft-sandbox/prog.org.ru/tree-example), который мы разбирали. Возьмите в качестве значения unique_ptr< QObject> и будет счастье. К слову, эта реализация у нас хорошо зашла для широкого использования.


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 18, 2020, 13:21
Либо у вас observer_ptr и вы айтемом не владеете (а значит и удалить не можете).
Либо у вас unique_ptr и вы c айтемом можете делать что хотите.
Возьмем самую банальную ситуацию. Есть окно, в нем список/listbox и рядом кнопка del которая становится активной если в listbox есть выбранные айтемы. При этом список будет стандартным классом, чем-то типа QTreeWidget, т.е. владеть-то он владеет, но что (какие айтемы) удалять - без понятия. Это чисто ф-ционал окна (напр случилось нажатие del). Окну и нужно удалять. И, как я понял, это придется делать так
Код
C++ (Qt)
void DeleteItem( ItemObserverPointer & item )
{
auto parent = item->parent();
assert(parent);
parent->remove(item);
item.reset();
}
 
Ну как-то не очень удобно (особенно по сравнению с простецким "delete item"). И кстати не видно где/как пресекается удаление непотопляемого root'a

Как нет уникальности, когда она есть? Если айтем в модели\вьюхе, то модель\вьюха уникально этим айтемом владеет.
Если под "уникальностью" подразумевается и "только вью удаляет" то (очень) простой пример выше показывает что это очевидно не так. Да, вью должно остаться валидным после удаления, но лучше это отслеживать в самом айтеме чем грузить caller'a всякий раз.

Ф-ционал "copy" - ушли в отказ, ф-ционал "take" - тоже не видно. Я понимаю что это скорее попытка написать "без всяких указателей/адресов". Теоретически это интересно, но с чисто утилитарной точки зрения (типа "поюзать") неудобства очевидны, а вот выгод не видно. Хотя не исключено что мнение старого ретрограда предвзято  :)


Название: Re: Qt + вумные указватели
Отправлено: ViTech от Февраль 18, 2020, 14:35
Ну как-то не очень удобно (особенно по сравнению с простецким "delete item").

Как Вы относитесь к идее не использовать в современном С++ new и delete? Т.е. для создания объекта использовать функции типа make_unique/shared, а удаляться объект будет, когда его умные указатели помрут.


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 18, 2020, 15:40
И, как я понял, это придется делать так

Код
C++ (Qt)
   ItemUniquePointer take(ItemObserverPointer item)
   {
       ItemUniquePointer result;
       const auto pred = [item](const ItemUniquePointer &p) { return item.get() == p.get(); };
       const auto it = std::find_if(m_children.begin(), m_children.end(), pred);
       if (it != m_children.end()) {
           std::swap(result, *it);
           result->m_parent = nullptr;
           m_children.erase(it);
       }
       return result;
   }
 
void DeleteItem( ItemObserverPointer & item )
{
   (void)item->parent()->take(item);
}
 

На самом деле, там есть метод children который дает залезть в кишки напрямую и просто достать ребенка из вектора но он не очень безопасен так как не зануляет парента и не соблюдает инвариант.

Ну как-то не очень удобно (особенно по сравнению с простецким "delete item"). И кстати не видно где/как пресекается удаление непотопляемого root'a

Ну а как вы рута собираетесь удалять? Метода выцепить рут у вьюхи нет, он на то и invisible root item что он всегда есть.
Метод clear() какой-нибудь да, можно добавить, который свапнет текущий рут с пустым и вуаля рут снова всегда есть.


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 19, 2020, 00:00
Я немного обновил код (https://github.com/ABBAPOH/textureviewer/blob/master/src/libs/utils/treemodelitem.h), в частности добавил методы take() и переименовал тайпдефы в более простые варианты (Pointer и Holder)


Название: Re: Qt + вумные указватели
Отправлено: ssoft от Февраль 19, 2020, 08:09
Я немного обновил код (https://github.com/ABBAPOH/textureviewer/blob/master/src/libs/utils/treemodelitem.h), в частности добавил методы take() и переименовал тайпдефы в более простые варианты (Pointer и Holder)

Смущает только использование не быстрого поиска дочернего элемента

Код
C++ (Qt)
const auto pred = [item](const ItemUniquePointer &p) { return item.get() == p.get(); };
const auto it = std::find_if(m_children.begin(), m_children.end(), pred);
 

Можно немного изменить ItemObserverPointer - реализовать его через итератор листа, с помощью которого и удалять дочерний элемент без всякого поиска.


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 19, 2020, 12:22

Смущает только использование не быстрого поиска дочернего элемента

Код
C++ (Qt)
const auto pred = [item](const ItemUniquePointer &p) { return item.get() == p.get(); };
const auto it = std::find_if(m_children.begin(), m_children.end(), pred);
 

Можно немного изменить ItemObserverPointer - реализовать его через итератор листа, с помощью которого и удалять дочерний элемент без всякого поиска.

Идея интересная, но проблема в том, что итераторы вектора не стабильны - при добавлении айтема в середину итераторы поедут.
Так-то можно просто в сам айтем положить его "позицию" (итератор или индекс) в паренте но см выше.


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 19, 2020, 12:36
Ну а как вы рута собираетесь удалять? Метода выцепить рут у вьюхи нет, он на то и invisible root item что он всегда есть.
До root'a можно добраться через parent().

Код
C++ (Qt)
void DeleteItem( ItemObserverPointer & item )
{
   (void)item->parent()->take(item);
}
 
А если parent'а нет? И занулять item наверно все-таки надо. Так это просто еще один вариант remove (симпатичнее)

Как Вы относитесь к идее не использовать в современном С++ new и delete? Т.е. для создания объекта использовать функции типа make_unique/shared, а удаляться объект будет, когда его умные указатели помрут.
Если уж так хочется принципиально не использовать указатели и адресную арифметику, то для этого есть др языки, напр жаба или пытон. Но сама по себе идея интересна, в конце-концов никто не заставляет использовать такой подход всегда и везде.

Ну хорошо, вот я использую голый и, естественно, получаю за это, напр
Код
C++ (Qt)
qDeleteAll(treeWidget->selectedItems());
Увы, так резво нельзя, это рухнет если и parent и child выбраны. Приходится сначала отфильтровать selection, не смертельно,  но заботы/хлопоты. Ожидается что в новой реализации этого не произойдет, ведь все голые истреблены! Но.. я не вижу как, по-моему проблемы все те же, и фильтровать все равно надо. Ну получим мы вектор ItemObserverPointer, но он (падла), не отслеживает валидность и по-прежнему указывает на айтем что парент давно грохнул.

Тогда где "выйгрышь"? Или я ошибаюсь?



Название: Re: Qt + вумные указватели
Отправлено: ViTech от Февраль 19, 2020, 13:07
Если уж так хочется принципиально не использовать указатели и адресную арифметику, то для этого есть др языки, напр жаба или пытон.

Зачем так радикально, если на С++ можно писать так же и даже лучше :).

...
Тогда где "выйгрышь"? Или я ошибаюсь?

Про дерево - отдельный разговор. Главное в умных указателях - это понятное управление временем жизни объекта. И бонусом - автоматическое "зануление" weak-указателей. Но вряд ли кто будет пихать в них автоматическое удаление из всевозможных сторонних контейнеров. Это другая задача, с другими реализациями и накладными расходами.

А "выйгрышь" можно увидеть на примере того же Car. В Вашей реализации есть утечки памяти: в main три раза выполнено new, и ни разу delete. Можете сказать, что в данном примере это несущественно, он про другое. Но, тем не менее, ситуация показательная: в Вашем варианте приходится задумываться, что и когда нужно/можно удалять, а что нельзя. Нужно удалять engine и car, и нельзя удалять engine2. Только из кода это ни фига не очевидно :). Для сравнения, попробуйте в моём варианте что-нибудь сломать, случайно или намеренно (естественно кроме хаков типа delete &*ptr и пр.). Можно, конечно, набираться профессионализма в отладчике, но лучше просто писать программы, у которых вероятность сломаться намного меньше :).


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 19, 2020, 14:21
Зачем так радикально, если на С++ можно писать так же и даже лучше :).
Конечно, "дай бог", но пока не очень  :)

Про дерево - отдельный разговор. Главное в умных указателях - это понятное управление временем жизни объекта. И бонусом - автоматическое "зануление" weak-указателей.
Не согласен с такими акцентами. Пресловутый "observer" никак не влияет на время жизни, но иметь с него валидность очень хотелось бы (см только что приведенный пример) 

Но вряд ли кто будет пихать в них автоматическое удаление из всевозможных сторонних контейнеров. Это другая задача, с другими реализациями и накладными расходами.
Другая. Но встречается часто и заметьте, обычно вкупе или рядом с вумными. И выясняется что сам по себе вумный не очень-то умен (валидность есть - и слава богу) - все равно приходится возиться с контейнерами связей (как и в данном случае)

Для сравнения, попробуйте в моём варианте что-нибудь сломать, случайно или намеренно (естественно кроме хаков типа delete &*ptr и пр.).
Цитировать
Какой хороший я - и песенка моя!
:)

Можно, конечно, набираться профессионализма в отладчике, но лучше просто писать программы, у которых вероятность сломаться намного меньше :).
Насколько хорош (надежен и.т.п.) мой код - это конечно имеет значение. Но далеко не первостепенное, эти проблемы не идут ни в какое сравнение с тем как вообще получить/написать работающий, ф-циональный код. Др словами обычно на старте я вообще НЕ ЗНАЮ что писать :) См раздел "Алгоритмы", хотя бы последняя (школьная) задачка. После N дней гугления, возможно, что-то и найдется, но можно ли этому верить и стоит ли связываться с реализацией - хз. Это потом уже, когда по сути все решено, можно и покалякать, а что там лучше "юник" или "шаред". В общем "писать программы" и "синтаксический сахар" - вещи очень разные  :)


Название: Re: Qt + вумные указватели
Отправлено: ViTech от Февраль 19, 2020, 15:17
Пресловутый "observer" никак не влияет на время жизни, но иметь с него валидность очень хотелось бы (см только что приведенный пример)

...
Код
C++ (Qt)
qDeleteAll(treeWidget->selectedItems());
Увы, так резво нельзя, это рухнет если и parent и child выбраны. Приходится сначала отфильтровать selection, не смертельно,  но заботы/хлопоты. Ожидается что в новой реализации этого не произойдет, ведь все голые истреблены! Но.. я не вижу как, по-моему проблемы все те же, и фильтровать все равно надо. Ну получим мы вектор ItemObserverPointer, но он (падла), не отслеживает валидность и по-прежнему указывает на айтем что парент давно грохнул.

Через "observer" нельзя напрямую удалять объект (использовать delete observer и вариации). Как Вам такая мысль? :)

В общем "писать программы" и "синтаксический сахар" - вещи очень разные  :)

Код
C++ (Qt)
Engine * Car::ReplaceEngine( Engine * engine )
{
   QMutexLocker lock(&mMutex);
   Q_ASSERT(engine && engine->GetOwner() == 0);
   Engine * old = mEngine.take();
   old->SetOwner(0);
 
   mEngine.reset(engine);
   engine->SetOwner(this);
 
   return old;
}
...
void Monitor::SetEngine( Engine * engine )
{
   QMutexLocker lock(&Car::mMutex);
   if (engine == mEngine) return;
 
   if (mEngine)
       mEngine->RemMonitor(this);
 
   mEngine = engine;
 
   if (mEngine)
       mEngine->AddMonitor(this);
}

vs

Код
C++ (Qt)
SingleEngine Car::replaceEngine(SingleEngine engine)
{
   using std::swap;
   swap(m_engine, engine);
   return engine;
}
...
void Monitor::setEngine(const WeakEngine& engine)
{ m_engine = engine; }

Как по мне, так очень сладкий сахар получается. Вам так не кажется? Впрочем, о вкусах, естественно, не спорят :).


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 19, 2020, 15:36
Через "observer" нельзя напрямую удалять объект (использовать delete observer и вариации). Как Вам такая мысль? :)
Кстати это "дыра" в идеологии "без new/delete" - а что делать если нужно именно "удалить"? Напр айтем из списка, таких случаев полно. Ну хорошо, напишем DeleteItem, обратимся к parent, не переломимся. Так ведь все равно рухнет :'(

Как по мне, так очень сладкий сахар получается. Вам так не кажется? Впрочем, о вкусах, естественно, не спорят :).
Да сладкий-то он может и сладкий (не мне судить, всегда пью чай без сахара). Только вот нужного ф-ционала он не имеет. Проблема/вопрос "а какие мониторы наблюдают за данной Engine?" возникнет очень быстро, и городить их контейнер (член Engine) придется. Как и отслеживать связки. А оставшиеся вумные указатели будут немым укором несостоявшейся вумности.


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 19, 2020, 16:39
Код
C++ (Qt)
   using std::swap;
 

Кстати начиная с какой-то версии плюсов это не нужно, можно просто std::swap звать, потому что теперь можно пегегружать std::swap и юзерский swap не нужен.


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 19, 2020, 16:47
Кстати это "дыра" в идеологии "без new/delete" - а что делать если нужно именно "удалить"? Напр айтем из списка, таких случаев полно. Ну хорошо, напишем DeleteItem, обратимся к parent, не переломимся. Так ведь все равно рухнет :'(

Я конечно понимаю желание стрелять в ногу, но почему оно должно рухнуть? У вас есть инвариант - либо айтем в дереве и у него есть парент (и его удалить нельзя), либо айтем не в дереве и с ним можно делать что угодно. Единственный способ перехода между инвариантами - добавление\убирание из дерева.
Всё. Других методов просто нет. "delete item" это не убирание из дерева, это удаление. И не надо нагружать удаление этой семантикой - это очень плохой подход.
Вы, наверное, никогда не писали код с исключениями (да откуда они в Qt программе, верно), но код вида new MyObject(this) скорее всего утечет, если ВНЕЗАПНО вылетит bad_alloc при ресайзе вектора детей. Хорошо, если где-то внутри у этот указатель завернется во временный QScopedPointer (что скорее всего не так), но вам не кажется странным заворачивать указатель в юник внутри конструктора если можно просто этот же самый юник создать на стороне юзера?


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 19, 2020, 16:49
У нас кстати вот есть точно такой кусок кода - объекты в конструкторе себя регистрируют в глобальном кэше, при удалении дерегистрируют.
И что вы думаете? Где что удаляется хрен пойми. И да, это говно течет. Ровно потому что где что удаляется хрен пойми.

Ну то есть - вот он есть (глобальный, прости хоспаде) кэш. Он владеет объектами? Вроде бы нет, он не подчищает что осталось в деструкторе и делетов в нем нет. Но при том если объект есть в кэше, то новый создавать не надо, надо взять из кэша. А кто владеет объектами? Да хрен пойми, они тут и там запрашиваются и никто не думает ничего удалять.

И вот я типа могу заюзать shared_ptr и хранить в кэше шареды - всё чики-пуки... Кроме тех мест где таки явно зовется delete object с целью удалить их из кэша


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 19, 2020, 17:09
но почему оно должно рухнуть?
Пусть выбраны 2 айтема и первый - парент второго
Код
C++ (Qt)
ItemObserverPointer item1 = ...;
ItemObserverPointer item2 = ...;
DeleteItem(item1);  // это удалит item1 и item2
DeleteItem(item2);  // а это вылет

И вот я типа могу заюзать shared_ptr и хранить в кэше шареды - всё чики-пуки... Кроме тех мест где таки явно зовется delete object с целью удалить их из кэша
Тогда кеш становится одним из владельцев, а значит хранимые там объекты живут forever. Городил велик на эту тему (в "кладовой")


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 19, 2020, 17:44
но почему оно должно рухнуть?
Пусть выбраны 2 айтема и первый - парент второго
Код
C++ (Qt)
ItemObserverPointer item1 = ...;
ItemObserverPointer item2 = ...;
DeleteItem(item1);  // это удалит item1 и item2
DeleteItem(item2);  // а это вылет

То есть вы сперва придумали функцию "как бы поудобнее" а потом нашли способ выстрелить себе ей в ногу? Ну что ж, поздравляю, у меня такой функции нет и я в ногу не стреляю. Совпадение? Не думаю.

Шутки шутки, никто не обещал что умные указатели не дадут выстрелить в ногу. Так-то можно сделать delete smart_ptr.get() и долго удивляться как же так вышло.
В вашем примере достаточно очевидно что проблема во временном хранилище - списке выделенных элементов.
По сути, вы итерируетесь по списку меняя его в процессе. Ну так фигли, хотите я вам такое же на std::vectorе напишу? Тоже будет падать. И будет падать независимо от того, вумные указатели в векторе или обычные.


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 19, 2020, 17:47
Ну или можно сделать что-то типа
Код:
{
    std::vector<ItemHolder> toDelete;
    for (const auto &item: view->selectedItems()) {
        toDelete.push_back(item->parent()->take(item));
    }
} // вот тут всё разрушилось

По сути, мы "раздербанили" дерево на отдельные айтемы сохраняя инвариант.


Название: Re: Qt + вумные указватели
Отправлено: ViTech от Февраль 19, 2020, 17:52
Кстати начиная с какой-то версии плюсов это не нужно, можно просто std::swap звать, потому что теперь можно пегегружать std::swap и юзерский swap не нужен.

Про такое слышал, но когда библиотека писалась, это может ещё не работало, точно не помню. Поэтому оставил по старинке, для надёжности. Подожду, когда это в С++20 утрясётся, там swap уже customization point обзывают.

У нас кстати вот есть точно такой кусок кода - объекты в конструкторе себя регистрируют в глобальном кэше, при удалении дерегистрируют.

Я вообще склоняюсь к тому, что объект не должен знать, под каким типом владения он находится, и не может сам создавать ассоциативные связи с собой, если ему не дали для этого достаточно данных. Т.е. shared_from_this не должно быть у объекта, максимум  weak_from_this, да и то под большим вопросом. Это сильно привязывает класс к типу владения. На Хабре недавно было: "Сказ об опасном std::enable_shared_from_this.. (https://habr.com/ru/post/478190/).". Без этих xxxx_from_this писать сложнее, но, возможно, правильнее :).


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 19, 2020, 18:27
Я вообще склоняюсь к тому, что объект не должен знать, под каким типом владения он находится, и не может сам создавать ассоциативные связи с собой, если ему не дали для этого достаточно данных. Т.е. shared_from_this не должно быть у объекта, максимум  weak_from_this, да и то под большим вопросом. Это сильно привязывает класс к типу владения. На Хабре недавно было: "Сказ об опасном std::enable_shared_from_this.. (https://habr.com/ru/post/478190/).". Без этих xxxx_from_this писать сложнее, но, возможно, правильнее :).

Вот тут полностью соглашусь, этот std::enable_shared_from_this выглядит очень убого и непродуманно.


Название: Re: Qt + вумные указватели
Отправлено: ssoft от Февраль 19, 2020, 21:49
Идея интересная, но проблема в том, что итераторы вектора не стабильны - при добавлении айтема в середину итераторы поедут.
Так-то можно просто в сам айтем положить его "позицию" (итератор или индекс) в паренте но см выше.

Насколько оправдано использование вектора здесь? Разве нужен рандомный доступ?
Преимущества std::list и его итераторов, которые не портятся со временем), здесь явно.


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 19, 2020, 23:15
Идея интересная, но проблема в том, что итераторы вектора не стабильны - при добавлении айтема в середину итераторы поедут.
Так-то можно просто в сам айтем положить его "позицию" (итератор или индекс) в паренте но см выше.

Насколько оправдано использование вектора здесь? Разве нужен рандомный доступ?
Преимущества std::list и его итераторов, которые не портятся со временем), здесь явно.

Конечно нужен вектор, это ж айтем _модели_, а у нее должен быть доступ к строке за О(1)


Название: Re: Qt + вумные указватели
Отправлено: ssoft от Февраль 20, 2020, 09:25
Конечно нужен вектор, это ж айтем _модели_, а у нее должен быть доступ к строке за О(1)

Тогда можно так

Код
C++ (Qt)
using Items = ::std::list< Item >;
using ItemForIndex = ::std:vector< Items::iterator >
 
Items m_items;
ItemForIndex m_item_for_index;
 


Название: Re: Qt + вумные указватели
Отправлено: ssoft от Февраль 20, 2020, 09:49
Пробовал (https://github.com/ABBAPOH/textureviewer/blob/master/src/libs/utils/treemodelitem.h). И уже дважды постил на форуме. Получается отлично.

Там случайно в методе createChild не ошибка?

Код
C++ (Qt)
   ItemPointer createChild(qsizetype row = -1)
   {
       insert(row == - 1 ? childCount() : row, std::make_unique<Derived>());
       return ItemPointer(m_children.back().get());
   }
 

При любом row возвращается последний из элементов. Не должно быть хотя бы так?

Код
C++ (Qt)
   ItemPointer createChild(qsizetype row = -1)
   {
       insert(row == - 1 ? childCount() : row, std::make_unique<Derived>());
       return child(row);
   }
 


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 20, 2020, 10:26
Да, действительно=) Сказывается отсутствие тестов, там еще и в remove был assert неправильный=)


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 20, 2020, 14:06
И будет падать независимо от того, вумные указатели в векторе или обычные.
Единственное хорошее в вумных указателях - это их валидность. Напр при использовании либы ViTech тамошний weak стал бы корректным null при удалении парента (ну это я так думаю). А если мне все равно рысью бегать и следить чтобы никто его не замочил - так проще голые.

Насколько оправдано использование вектора здесь? Разве нужен рандомный доступ?
Преимущества std::list и его итераторов, которые не портятся со временем), здесь явно.
Ну хорошо, допустим под видом обсервера Вы храните итератор list (как я понял). Но как Вы обеспечите его валидность? Др словами
Код
C++ (Qt)
ItemPointer p = ...;  // получили валидный айтем
...
// тут может происходить что угодно, возможно айтем грохнут
---
if (!p.isNull()) {
 
Сработает эта проверка?

Немного о другом. Я думал что юники класть в контейнер нельзя (начинает вякать что нельзя копировать). Но судя по коду Авварон - оказывается можно! Поясните подробности. Спасибо
 


Название: Re: Qt + вумные указватели
Отправлено: ssoft от Февраль 20, 2020, 14:35
Код
C++ (Qt)
...
// тут может происходить что угодно, возможно айтем грохнут
---
 

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


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 20, 2020, 16:59
Айтем грохнуть может только родительский айтем. ItemPointer в данном случае не должен предоставлять такую возможность.
Как же он не даст если parent() метод public? А если не так - то как "ваще" удалять? (такие очевидные вопросы и озвучивать неудобно :))


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 20, 2020, 17:07
Немного о другом. Я думал что юники класть в контейнер нельзя (начинает вякать что нельзя копировать). Но судя по коду Авварон - оказывается можно! Поясните подробности. Спасибо


В кутешные (с коровой, COW) нельзя потому что любая неконстантная операция потенциально может детачить, то есть делать deep copy, то есть требует наличия конструктора копирования у контейнера и, как следствие, у T.
Контейнеры стандартной библиотеки используют конструктор копирования Т только в своем конструкторе копирования. То есть пока пользователь явно не захочет скопировать контейнер, компилятору глубоко по барабану, копируется Т или нет.
Вектор юников можно спокойно мувать в функцию или возвращать из функции по значению.
То же самое относится и к конструктору по умолчанию у Т, некоторые кутешные контейнеры требуют его наличия, когда стандартным пофиг.


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 20, 2020, 17:16
Др словами
Код
C++ (Qt)
ItemPointer p = ...;  // получили валидный айтем
...
// тут может происходить что угодно, возможно айтем грохнут
---
if (!p.isNull()) {
 
Сработает эта проверка?

Не сработает и не должна.
Вас _уже_ защитили от явного вызова delete использованием обсервера. Просто так взять и "грохнуть" айтем не получится - надо айем "достать" из дерева.
Причем после "доставания" можете считать, что исходный ItemPointer невалидный - теперь у вас новый хэндл, который вернул take() и вы теперь пользуетесь этим хэндлом.
Как std::vector::erase(), один в один. Вызвали деструктивную операцию - извольте обновить итераторы.
Вы упорно пытаетесь хранить стейт (хотя бы выше по стеку) и находите всё новые и новые баги ругая всё подряд. Не приходило в голову, что проблема не в "вумных" указателях, которые "недостаточно мощны", а в хранении стейта?
Вы пытаетесь найти решение проблемы которую сами себе создали - "а вот у меня всё от всего зависит и всё на всё ссылается, какую бы таблетку мне принять, чтобы голова не болела".


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 20, 2020, 17:19
Тогда можно так

Код
C++ (Qt)
using Items = ::std::list< Item >;
using ItemForIndex = ::std:vector< Items::iterator >
 
Items m_items;
ItemForIndex m_item_for_index;
 

Так всё равно же вынимание за О(N).

Я хочу напомнить, что "вынимание" айтема из середины вектора само по себе O(N), так что линейный поиск не так страшен.
А вот range "вынимание" было бы интересно добавить. Но это ж надо делать итераторы по айтему=)


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 21, 2020, 12:05
Вектор юников можно спокойно мувать в функцию или возвращать из функции по значению.
Т.е. сработает лишь конструктор перемещения (а он у юника имеется)
То же самое относится и к конструктору по умолчанию у Т, некоторые кутешные контейнеры требуют его наличия, когда стандартным пофиг.
Наверно речь идет о "некоторых" контейнерах, напр std::vector.resize() потребует дефаултный конструктор

Вы упорно пытаетесь хранить стейт (хотя бы выше по стеку) и находите всё новые и новые баги ругая всё подряд.
Не совсем так, сохранить state я не пытаюсь, против изменения итераторов возражений нет. НО валидность (isNull) должна быть, иначе это дискредитирует саму идею вумных указателей. Ну в самом деле, нафиг они нужны если имею все те же проблемы голых?

Не сработает и не должна.
А мне кажется обеспечить валидность можно. И "без великов". Попробуем так
Код
C++ (Qt)
struct CIem {
typedef std::weak_ptr<CItem>  TItemPtr;
...
 
private:
TItemPtr m_parent;
std::vector<std::shared_ptr<CItem>> m_children;
};
 
Конечно юзать шаред не есть хорошо. Но мы постараемся зажать его в рамках класса. Здесь так не выходит - никак не объявить root (или take). Поэтому предлагаю так
Код
C++ (Qt)
struct CIem {
typedef std::weak_ptr<CItem>  TItemPtr;
...
 
private:
TItemPtr m_parent;
std::vector<TItemPtr> m_children;
 
typedef std::list<std::shared_ptr<CItem>> TList;
TList m_list;
TList::iterator m_iterator;
};
 
m_list хранит шаред чилдренов, а если это root - то и шаред на себя. Тогда
Код
C++ (Qt)
// static
bool CItem::DeleteItem( TItemPtr itemPtr )
{
 auto item = itemPtr.lock();
 if (!item) return false;
 auto parent = item->parent().lock();
 if (parent)
   parent->removeChild(item.get());
 else
  item->m_list.erase(m_iterator);
 return true;
}
 
CItem::~CItem( void )
{
 while (m_children.size())
   DeleteItem(m_children.front());
}
Попинайте


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 21, 2020, 14:24
Наверно речь идет о "некоторых" контейнерах, напр std::vector.resize() потребует дефаултный конструктор

Очевидно, std::vector.resize() требует default ctor. Но как и с копированием, пока вы его не зовете, никто ничего не требует. А вот QVector зовет метод defaultConstruct из метода reallocData который зовется на любой чих => требование T() расширяется с одного метода на весь контейнер.

А мне кажется обеспечить валидность можно. И "без великов"

Вы не понимаете, зачем вообще нужен ObserverPointer. Он нужен ради одной-единственной цели - отвечать "нет" на вопрос "а нужно ли мне звать delete на этот указатель". Когда у вас есть метод T *getT() совершенно неясно (без чтения документации) что с этим T делать - вы им владеете? можете его удалять? должны его удалять?
Методы вида
Код:
std::unique_ptr<T> getT();
ObserverPointer<T> getT();
самодокументированны - в первом случае вы становитесь владельцем, во втором нет. Не надо писать обширных трактатов на тему кто становится владельцем как это делают в доке Qt (и при этом один и тот же метод addFoo(Foo *foo) в разных классах ведет себя по разному (QWidget::addAction/QLayout::addItem).
Больше обсервер ни зачем не нужен - это абсолютно обычный "глупый" указатель со всеми вытекающими.
И паттерн использования ровно такой же как с голым указателем или итератором. Не надо пытаться наделить "глупый" observer ненужной семантикой, его не для этого придумали.
Хотите зануление - есть shared/weak и только эта пара может обеспечить безопасность в multi-thread. Но они нафиг не нужны в задаче с деревом. У меня нет проблемы проследить за временем жизни айтемов потому что паттерны использования достаточно простые - добавить новый айтем в конец, отсортировать айтемы, удалить айтем.


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 21, 2020, 14:29
А вот кстати на самом деле где not_null может пригодиться - хранить не вектор юников, а вектор нот_нулл юников, иначе имея неконстантный метод children() глупый пользователь может вызвать .reset() на элементе вектора, нарушив инвариант.
Причем сделать элементы константными нельзя, потому что тогда не получится пересорировать (ибо надо свапать элементы) вектор


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 22, 2020, 12:58
Вы не понимаете, зачем вообще нужен ObserverPointer
Ну почему обязательно "не понимает" (не знает, не читал и.т.п  :))? Может и понимает, но не считает это хорошим , правильным, имеет др мнение. Такой вариант вполне возможен :)

Лично я думаю что указатель никак не может считаться "вумным" если у него нет проверки валидности. И, видимо, не я один если обсервер "experimental".

есть shared/weak и только эта пара может обеспечить безопасность в multi-thread.
Это лишь атомарная блокировка удаления, как бы "необходимое", но совсем не "достаточное", никакой безопасности она сама по себе еще не обеспечивает. Ну сработал lock(), т.е. "жизнь продлена", но ведь это может случиться в N нитках.

Но они нафиг не нужны в задаче с деревом. У меня нет проблемы проследить за временем жизни айтемов потому что паттерны использования достаточно простые - добавить новый айтем в конец, отсортировать айтемы, удалить айтем.
Ну допустим. Но так ли уж страшен вариант с полноценным weak (вместо калечного обсервера)? Не перемудрил ли я в прошлый раз? А если так
Код
C++ (Qt)
struct CIem {
typedef std::weak_ptr<CItem>  TItemPtr;
...
 
private:
TItemPtr m_parent;
std::vector<std::shared_ptr<CItem>> m_children;
std::shared_ptr<CItem> m_self;    // not null for root
};
 
Ну и вроде все то же что у Вас, ну чуть за root'ом присмотреть


Название: Re: Qt + вумные указватели
Отправлено: ViTech от Февраль 22, 2020, 13:51
А вот кстати на самом деле где not_null может пригодиться - хранить не вектор юников, а вектор нот_нулл юников, ...

Правильно. Осталось только выяснить, как будет себя чувствовать вектор с неперемещаемыми not_null<unique> :).

Лично я думаю что указатель никак не может считаться "вумным" если у него нет проверки валидности. И, видимо, не я один если обсервер "experimental".

Есть такое мнение: Abandon observer_ptr (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1408r0.pdf).


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 22, 2020, 23:25

Есть такое мнение: Abandon observer_ptr (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1408r0.pdf).

Почему-то напомнило когда старый дед за столом начинает по пьяни травить байки.
Ну да, gsl::owner (ака T*) отличная замена observer_ptr =)
Страуструп верно говорит что "неовнящий" указатель - это самый частый кейз, вот только "овнящего" указателя в программе на современном с++ быть не должно, а gsl::owner это костыль придуманный мелкософтом и прочими монстрами у которых нет возможности и желания переписать всё на "вумники" а развивать и поддерживать это говно надо.
Кстати хочу заметить что обсервер (был?) бесконечно ближе к стандартизации чем gsl::owner=) (в отличие от gsl::span (https://en.cppreference.com/w/cpp/container/span))

Впрочем, не понимаю, чего к нему так прицепились, наружу вполне может торчать обычный T* в данной задаче о чем я уже писал - обсервер вообще ничем не отличается от Т*.
Вся "вумность" тут вокруг юников, а чем мы наружу смотрим вообще не важно - ровно один и тот же код был бы без "вумников" на голых указателях.


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 22, 2020, 23:44
Не перемудрил ли я в прошлый раз? А если так
Код
C++ (Qt)
struct CIem {
typedef std::weak_ptr<CItem>  TItemPtr;
...
 
private:
TItemPtr m_parent;
std::vector<std::shared_ptr<CItem>> m_children;
std::shared_ptr<CItem> m_self;    // not null for root
};
 
Ну и вроде все то же что у Вас, ну чуть за root'ом присмотреть

То есть предлагается делать delete item который сам на себя держит шаред. Я всё верно понял?


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 23, 2020, 10:26
То есть предлагается делать delete item который сам на себя держит шаред. Я всё верно понял?
Да. И подозреваю не мне первому это пришло в голову, сейчас тыкнут в нос какой-нибудь хабровской статейкой  :)

Вся "вумность" тут вокруг юников,
Да, и если бы было "по уму" (можно брать weak от юника) то все совершенно очевидно, и проблемы нет.

..а чем мы наружу смотрим вообще не важно - ровно один и тот же код был бы без "вумников" на голых указателях.
Вообще-то важно, задумка (как я понял) была работать "без указателей/ссылок", с наглой мордой подавая/возвращая все по значению. Поэтому про голые надо забыть.

Кстати тут упоминалось что weak принципиально не имеет get(), а только lock(). Это выглядит очень "идейным", мол, написать неправильно просто не получится! Но по жизни есть масса ситуаций когда multi-threading просто не интересует, и навязанный lock() очень утомителен. Заметим что в (якобы отсталой) Qt реализации это учитывается.

Из этих соображений обсервер надо оставить, но конечно иметь и геттеры "по всем правилам", напр LockItem(). Тогда все хорошо.


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 23, 2020, 20:10
Из этих соображений обсервер надо оставить, но конечно иметь и геттеры "по всем правилам", напр LockItem(). Тогда все хорошо.

observer это ужасное имя которое не раскрывает смысла указателя. По сути, такой "глупый" указатель это аналог view только для овнящего указателя (и да, ptr_view уже предлагалось (https://wgml.pl/blog/object_ptr.html)).
Кто-то может сказать что std::string_view или std::span не нужны, ведь есть же голый указатель и длинна. Ну а чо, передавайте в функцию указатель да длину как деды делали, нафига сущности плодить?
Вот обсервер ровно для этого и нужен - давать "вью" на указатель (а ВСЕ view не безопасны и никакое удаление не отслеживают, они НЕ ЗА ЭТИМ нужны). Причем не только на юник но и на шаред.
Вы же всё пытаетесь решить проблему которую сами себе устроили "как бы мне вызвать делет на айтем чтобы ничего не покрашилось". Никак, в современной программе на с++ вообще нет new и delete. Когда холдер вышел из скопа, тогда и удалили. Желание вызвать магическую функцию которая удалит айтем, найдет его в паренте, вычистит его оттуда, пройдется по десятку хеш таблиц куда вы этот айтем сложили, такое желание, сорян, утопично.


Название: Re: Qt + вумные указватели
Отправлено: ViTech от Февраль 24, 2020, 12:00
Почему-то напомнило когда старый дед за столом начинает по пьяни травить байки.
Ну да, gsl::owner (ака T*) отличная замена observer_ptr =)
Страуструп верно говорит что "неовнящий" указатель - это самый частый кейз, вот только "овнящего" указателя в программе на современном с++ быть не должно, а gsl::owner это костыль придуманный мелкософтом и прочими монстрами у которых нет возможности и желания переписать всё на "вумники" а развивать и поддерживать это говно надо.
Кстати хочу заметить что обсервер (был?) бесконечно ближе к стандартизации чем gsl::owner=) (в отличие от gsl::span (https://en.cppreference.com/w/cpp/container/span))

Да, с таким gsl::owner (ака T*)... как говорится: "Кому и кобыла невеста." )). Скопируется такой owner несколько раз, столько же раз память кто-то освободит. Веселье обеспечено.

Впрочем, не понимаю, чего к нему так прицепились, наружу вполне может торчать обычный T* в данной задаче о чем я уже писал - обсервер вообще ничем не отличается от Т*.
Вся "вумность" тут вокруг юников, а чем мы наружу смотрим вообще не важно - ровно один и тот же код был бы без "вумников" на голых указателях.

observer_ptr хоть и очень похож на голый указатель, но может уберечь от подобной чуши:
Код
C++ (Qt)
#include <experimental/memory>
 
int main()
{
   using namespace std;
   using namespace std::experimental;
 
   auto u = make_unique<int>(42);
 
   int* p = u.get();
   unique_ptr<int> a{p}; // Compiled.
   delete p;             // Compiled.
 
   observer_ptr<int> o{u.get()};
   unique_ptr<int>   b{o}; // Not compiled.
   delete o;               // Not compiled.
}
Вероятность, что в коде где-то проскочит unique_ptr<int> a{p} хоть и маленькая, но не нулевая.

Вот обсервер ровно для этого и нужен - давать "вью" на указатель (а ВСЕ view не безопасны и никакое удаление не отслеживают, они НЕ ЗА ЭТИМ нужны).

Хоть умные указатели не обязаны отслеживать удаление, такая функциональность может быть полезна. Созданные связи между объектами нужно разрывать, когда один из них удаляется. Взять тот же QLabel::buddy. Когда дружбан удаляется, buddy в QLabel должен занулиться. Теоретически правильный путь - это рассылка сообщений (дружбан погиб) и реакция на них (занулить, возложить цветочки). Но не всегда есть возможность делать коннекты, рассылать сообщения и т.п. И даже когда такая возможность есть, то, если надо только занулить обозревателя и больше ничего делать не нужно, использование QPointer или другого самозануляющегося weak может быть эффективнее, чем создавать коннекты и рассылать сообщения. Так что я не стал бы так просто отмахиваться от возможности самозанулиться для weak. Особенно в многопоточности.

Вся "вумность" тут вокруг юников,
Да, и если бы было "по уму" (можно брать weak от юника) то все совершенно очевидно, и проблемы нет.

Не все weak должны быть trackable (самозануляться, иметь проверку на валидность). Когда есть гарантии, что владелец живёт дольше обозревателя, такая функциональность может быть избыточной, и тогда достаточно голого указателя (обёртки на ним). Потому что за функциональность "проверка на валидность" нужно платить дополнительными операциями в runtime, что не всегда оправдано.

Так что weak/observer всякие нужны, и умные и простые, чтобы можно было выбрать оптимальный вариант исходя из конкретной ситуации.

Кстати тут упоминалось что weak принципиально не имеет get(), а только lock(). Это выглядит очень "идейным", мол, написать неправильно просто не получится! Но по жизни есть масса ситуаций когда multi-threading просто не интересует, и навязанный lock() очень утомителен. Заметим что в (якобы отсталой) Qt реализации это учитывается.

То, что реализация Qt позволяет отстрелить себе ноги - так себе аргумент. А чтобы lock() не утомлял, нужны унифицированные алгоритмы доступа (https://gitlab.com/CppObjectTokens/Module/Library/CppOtl/-/blob/master/doc/en/Access.md#unified-if-else). И вот такая штука была бы полезна: Initial Idea of Indirect If Statement (https://lists.isocpp.org/std-proposals/2019/10/0606.php).


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 24, 2020, 13:15
Вы же всё пытаетесь решить проблему которую сами себе устроили "как бы мне вызвать делет на айтем чтобы ничего не покрашилось". Никак, в современной программе на с++ вообще нет new и delete. Когда холдер вышел из скопа, тогда и удалили. Желание вызвать магическую функцию которая удалит айтем, найдет его в паренте, вычистит его оттуда, пройдется по десятку хеш таблиц куда вы этот айтем сложили, такое желание, сорян, утопично.
Так Вы (как и всякий другой) такую ф-цию все равно писать будете, деваться-то некуда. Вот есть "айтем" - пусть хоть тот же Ваш обсервер. Надо удалить его из списка. Таскать за собой индекс - несерьезно. Надеяться на выход из скопа не приходится. Ваши действия?

Ну ладно, а что все-таки "отдавать наружу" чтобы удобно было там его юзать? Может так
Код
C++ (Qt)
TreeItemRef item = treeView->Root().childAt(0);
 
item.SetName("test");     // синтаксис или "семантика" (?) ссылки
if (CheckSomething(item))
DeleteItem(item);
 
if (!item.IsNull())
item.SetName("done");
 
if (item.Lock()) {
...
}
 
По-моему симпатично и ф-ционально. Хочу только проверяю, хочу лочу, хочу ничего. Удобно и гибко. Но чем должен быть такой TreeItemRef ? Очевидно обсервер не годится, у него нет нужного ф0ционала. Но weak_ptr тоже будет неудобен и громоздок. Нужен "сахар", обертки, и все такое

Вот обсервер ровно для этого и нужен
Это все давно поняли, не тратьте свое время поясняя это снова и снова. Но ведь цель написания - достичь полезного рез-та, а не использовать "максимально подходящий" стандартный класс (за неимением другого).


Название: Re: Qt + вумные указватели
Отправлено: Old от Февраль 24, 2020, 13:51
По-моему симпатично и ф-ционально. Хочу только проверяю, хочу лочу, хочу ничего. Удобно и гибко.
По вашему конечно. :)
А что должно произойти симпатичного и функционального при выполнении этого?
Код
C++ (Qt)
if (item.Lock()) {
...
}
 
Что должно залочиться и как? Кто сделает unlock, когда? Чем отличается залоченный итем от не залоченного?

А в остальном этот набор букв очень симпатичный. :)


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 24, 2020, 16:39
Это все давно поняли, не тратьте свое время поясняя это снова и снова. Но ведь цель написания - достичь полезного рез-та, а не использовать "максимально подходящий" стандартный класс (за неимением другого).

Но вы до сих пор не поняли, что DeleteItem это плохой, негодный метод :)


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 24, 2020, 17:01
Но вы до сих пор не поняли, что DeleteItem это плохой, негодный метод :)
Compare to ?  :)


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 24, 2020, 17:41
Compare to ?  :)


Как Вы относитесь к идее не использовать в современном С++ new и delete? Т.е. для создания объекта использовать функции типа make_unique/shared, а удаляться объект будет, когда его умные указатели помрут.


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 25, 2020, 09:54

Как Вы относитесь к идее не использовать в современном С++ new и delete? Т.е. для создания объекта использовать функции типа make_unique/shared, а удаляться объект будет, когда его умные указатели помрут.
Это мы уже обсуждали
Код
C++ (Qt)
// int index
treeView->RemoveChildAt(index);
Против этого кода возражений нет (поправьте если не так). Но удалять "только так" не получится, неизбежна ситуация где индекса нет, а есть лишь "невладеющий" указатель. Т.е. никто умирать не собирался, надо мочить.

О другом (и более интересном). По-моему TreeItemRef должен быть инстансом такого
Код
C++ (Qt)
template<class T>
struct CItemRef {
...
// data
 std::shared_ptr<T> m_shared;
 std::weak_ptr<T> m_weak;
 T * m_raw;
};
typedef CItemRef<TreeItem> TreeItemRef;
 
И вроде это даже thread-safe. Или нет? Покритикуйте


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 25, 2020, 16:17
Код
C++ (Qt)
template<class T>
struct CItemRef {
...
// data
 std::shared_ptr<T> m_shared;
 std::weak_ptr<T> m_weak;
 T * m_raw;
};
typedef CItemRef<TreeItem> TreeItemRef;
 
И вроде это даже thread-safe. Или нет? Покритикуйте

И что есть что?


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 25, 2020, 17:19
И что есть что?
Код
C++ (Qt)
template<class T>
struct CItemRef {
 CItemRef( std::shared_ptr<T> sh )  :
   m_weak(sh),
   m_raw(sh.get())
 {
 }
 
 T * data( void ) const
 {
   auto p = m_shared.get();  // locked?
   if (p) return p;
 
   if (m_raw)
     assert(!m_weak.expired());
 
   return m_raw;
 }
 
...
// data
 std::shared_ptr<T> m_shared;
 std::weak_ptr<T> m_weak;
 T * m_raw;
};

Edit: убрал "ненужные тонкости" в конструкторе


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 25, 2020, 17:44
Код
C++ (Qt)
template<class T>
   m_shared.reset();
 

Не понял


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 26, 2020, 10:44
Не понял
shared не заполняем, он только для лока. Померещилось что шаред аргумент может меняться, но это не так - он подается по значению. Исправил

Конечно можно обойтись и без члена шаред, напр метод lock возвращает шаред, но класс его не хранит. Дело вкуса, мне больше нравится так, класс "самодостаточен", никакие др вумники (снаружи) не привлекаются.


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 26, 2020, 15:41
Код
C++ (Qt)
template<class T>
   auto p = m_shared.get();  // locked?
   if (p) return p;
 
 }
 

Тогда m_shared всегда содержит 0.


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 26, 2020, 15:50
Тогда m_shared всегда содержит 0.
Не тормозите
Код
C++ (Qt)
bool Lock( void )
{
 if (!m_shared.get())
  m_shared = m_weak.lock();
 
 return m_shared.get();
}


Название: Re: Qt + вумные указватели
Отправлено: Авварон от Февраль 26, 2020, 18:05
ОК, след вопрос, откуда мы возьмем шаред, если вы хотите удалять по raw-указателю?


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 27, 2020, 11:20
ОК, след вопрос, откуда мы возьмем шаред, если вы хотите удалять по raw-указателю?
Из самого айтема, шаред или в паренте или (для рута) m_self
Код
C++ (Qt)
struct CIem {
...
 
private:
TreeItemRef m_parent;
std::vector<std::shared_ptr<CItem>> m_children;
std::shared_ptr<CItem> m_self;    // not null for root
};
Не зациклится ли деструктор? Ну по-любому, даже в худшем случае, это решается через custom deleter. Но думаю (не проверял) этой проблемы не возникнет вообще. Когда шаред удаляет данные (нет больше владельцев), он сначала должен поставить указатель на данные = 0 (чтобы никто не успел схватить), а потом звать deleter. Поэтому когда дело дойдет до деструктора m_self - он уже нулевой.


Название: Re: Qt + вумные указватели
Отправлено: ViTech от Февраль 27, 2020, 11:37
Ну ладно, а что все-таки "отдавать наружу" чтобы удобно было там его юзать? Может так
Код
C++ (Qt)
TreeItemRef item = treeView->Root().childAt(0);
 
item.SetName("test");
if (CheckSomething(item))
DeleteItem(item);
 
if (!item.IsNull())
item.SetName("done");
 
if (item.Lock()) {
...
}

Но weak_ptr тоже будет неудобен и громоздок. Нужен "сахар", обертки, и все такое

То Вы чай без сахара (http://www.prog.org.ru/index.php?topic=32622.msg241251#msg241251) пьёте, то Вам weak_ptr громоздкий :).

Код
C++ (Qt)
if (auto item_data = item.lock())
{
   item_data->doSomething();
   ...
}
Что тут громоздкого?


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 27, 2020, 12:55
Код
C++ (Qt)
if (auto item_data = item.lock())
{
   item_data->doSomething();
   ...
}
Что тут громоздкого?
Да все. Невдобняк капитальный. Ведь вся содержательная часть - вызовы типа doSomething, все должно быть заточено под них. А тут мне надо заводить еще переменную, и/или как-то группировать по локу и.т.п. - та ну нафиг

И, как ни странно, мне кажется это куда менее "безопасным". Застрянет где-то item_data - и я могу долго парить айтемы что уже обречены.


Название: Re: Qt + вумные указватели
Отправлено: ViTech от Февраль 27, 2020, 13:15
И, как ни странно, мне кажется это куда менее "безопасным". Застрянет где-то item_data - и я могу долго парить айтемы что уже обречены.

А с item.Lock()/Unlock() меньше возни и никогда ничего не застрянет :). И item.data() в Release сборке никогда не вылетит. Ну дело хозяйское.


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 28, 2020, 07:44
А с item.Lock()/Unlock() меньше возни и никогда ничего не застрянет :).
Возни не меньше, но самих item.Lock()/Unlock() гораздо меньше, т.к. они делаются только по моему желанию
И item.data() в Release сборке никогда не вылетит.
Вылет - случай не такой уж плохой по сравнению с "молотит полную фигню" или "тормозит". Бездумный lock() провоцирует такие тяжелые ошибки.

В общем, не все так просто как пишут в книжках  :)


Название: Re: Qt + вумные указватели
Отправлено: Old от Февраль 28, 2020, 09:36
В общем, не все так просто как пишут в книжках  :)
Я не знаю какие книжки вы читаете, но непросто становится, когда делают какую-то дичь, как в данном случае.

Нельзя удобно совместить обычные и умные указатели, никак.
Если переделывать дерево, то shared_ptr для этого замечательно подходит. И позволяет закрыть все юзкейсы, которые вы здесь пытаетесь решить через жопу.
Также несложно будет сделать дерево, с которым можно будет безопасно работать из разных потоков.


Название: Re: Qt + вумные указватели
Отправлено: Igors от Февраль 29, 2020, 11:14
Выдохлись :) Попробую подвести некоторые итоги.

Значит удалять айтем (имея вумный указатель на него) нечем, стандартные средства этого не обеспечивают, придется великом.

Может это единственная проблема? Думаю что нет. У меня немало случаев когда голые указатели на айтемы хранятся в мапе как key или value. Не вижу как менять их на вумные (какие ?). Ну ясно шаред сразу разрушает весь ф-ционал. К сожалению, не проходит и weak. Даже если перетерпеть занудный лок - сравнение мапы перестанет работать. Остается "фиговый листочек" обсервер - ну, мягко говоря, несолидно



Название: Re: Qt + вумные указватели
Отправлено: Old от Февраль 29, 2020, 12:01
Выдохлись :) Попробую подвести некоторые итоги.
Просто не забывайте вдыхать. :)

Значит удалять айтем (имея вумный указатель на него) нечем, стандартные средства этого не обеспечивают, придется великом.
Это не откуда не следует. Откуда этот глупый вывод?

Может это единственная проблема?
Проблемы есть исключительно у вас, в связи с непониманием работы умных указателей.

Ну ясно шаред сразу разрушает весь ф-ционал. К сожалению, не проходит и weak.
Какой функционал разрушает шаред? Каким образом? :)
Почему не проходит и weak? Для чего он должен пройти? :)

Как обычно, было сделано куча глупых выводов на основании своей безграмотности. :)

Ну что, "деревянным айтемом" мы опять обсудили, предлагаю переходить к очередному обсуждению undo или финдреплейс. Давненько не возвращались. :)


Название: Re: Qt + вумные указватели
Отправлено: ViTech от Февраль 29, 2020, 12:23
Значит удалять айтем (имея вумный указатель на него) нечем, стандартные средства этого не обеспечивают, придется великом.

Никто за Вас не напишет дерево с нужными Вам характеристиками. Выше предлагали два варианта, которыми люди пользуются. И Вы ими воспользуйтесь, или опишите, что Вы считаете "стандартным" деревом, которое подойдёт для большинства случаев (как std::vector для contiguous контейнера).

У меня немало случаев когда голые указатели на айтемы хранятся в мапе как key или value. Не вижу как менять их на вумные (какие ?). Ну ясно шаред сразу разрушает весь ф-ционал. К сожалению, не проходит и weak. Даже если перетерпеть занудный лок - сравнение мапы перестанет работать.

How can I use a std::map with std::weak_ptr as key? (https://stackoverflow.com/questions/12875652/how-can-i-use-a-stdmap-with-stdweak-ptr-as-key)