Russian Qt Forum
Март 28, 2024, 15:25 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 2 3 [4] 5 6   Вниз
  Печать  
Автор Тема: Qt + вумные указватели  (Прочитано 26623 раз)
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3257


Просмотр профиля
« Ответ #45 : Февраль 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. Но они нафиг не нужны в задаче с деревом. У меня нет проблемы проследить за временем жизни айтемов потому что паттерны использования достаточно простые - добавить новый айтем в конец, отсортировать айтемы, удалить айтем.
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3257


Просмотр профиля
« Ответ #46 : Февраль 21, 2020, 14:29 »

А вот кстати на самом деле где not_null может пригодиться - хранить не вектор юников, а вектор нот_нулл юников, иначе имея неконстантный метод children() глупый пользователь может вызвать .reset() на элементе вектора, нарушив инвариант.
Причем сделать элементы константными нельзя, потому что тогда не получится пересорировать (ибо надо свапать элементы) вектор
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #47 : Февраль 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'ом присмотреть
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #48 : Февраль 22, 2020, 13:51 »

А вот кстати на самом деле где not_null может пригодиться - хранить не вектор юников, а вектор нот_нулл юников, ...

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

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

Есть такое мнение: Abandon observer_ptr.
Записан

Пока сам не сделаешь...
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3257


Просмотр профиля
« Ответ #49 : Февраль 22, 2020, 23:25 »


Есть такое мнение: Abandon observer_ptr.

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

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

Сообщений: 3257


Просмотр профиля
« Ответ #50 : Февраль 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 который сам на себя держит шаред. Я всё верно понял?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #51 : Февраль 23, 2020, 10:26 »

То есть предлагается делать delete item который сам на себя держит шаред. Я всё верно понял?
Да. И подозреваю не мне первому это пришло в голову, сейчас тыкнут в нос какой-нибудь хабровской статейкой  Улыбающийся

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

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

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

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

Сообщений: 3257


Просмотр профиля
« Ответ #52 : Февраль 23, 2020, 20:10 »

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

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

Сообщений: 858



Просмотр профиля
« Ответ #53 : Февраль 24, 2020, 12:00 »

Почему-то напомнило когда старый дед за столом начинает по пьяни травить байки.
Ну да, gsl::owner (ака T*) отличная замена observer_ptr =)
Страуструп верно говорит что "неовнящий" указатель - это самый частый кейз, вот только "овнящего" указателя в программе на современном с++ быть не должно, а gsl::owner это костыль придуманный мелкософтом и прочими монстрами у которых нет возможности и желания переписать всё на "вумники" а развивать и поддерживать это говно надо.
Кстати хочу заметить что обсервер (был?) бесконечно ближе к стандартизации чем gsl::owner=) (в отличие от gsl::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() не утомлял, нужны унифицированные алгоритмы доступа. И вот такая штука была бы полезна: Initial Idea of Indirect If Statement.
« Последнее редактирование: Февраль 24, 2020, 12:09 от ViTech » Записан

Пока сам не сделаешь...
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #54 : Февраль 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 тоже будет неудобен и громоздок. Нужен "сахар", обертки, и все такое

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

Сообщений: 4349



Просмотр профиля
« Ответ #55 : Февраль 24, 2020, 13:51 »

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

А в остальном этот набор букв очень симпатичный. Улыбающийся
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3257


Просмотр профиля
« Ответ #56 : Февраль 24, 2020, 16:39 »

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

Но вы до сих пор не поняли, что DeleteItem это плохой, негодный метод Улыбающийся
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #57 : Февраль 24, 2020, 17:01 »

Но вы до сих пор не поняли, что DeleteItem это плохой, негодный метод Улыбающийся
Compare to ?  Улыбающийся
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3257


Просмотр профиля
« Ответ #58 : Февраль 24, 2020, 17:41 »

Compare to ?  Улыбающийся


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

Сообщений: 11445


Просмотр профиля
« Ответ #59 : Февраль 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. Или нет? Покритикуйте
Записан
Страниц: 1 2 3 [4] 5 6   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.196 секунд. Запросов: 22.