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

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: Обобщить взаимодействие  (Прочитано 6752 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« : Май 09, 2016, 11:18 »

Добрый день

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

Есть довольно развесистый класс Trigger все экземпляры которого хранятся в глобальном контейнере. Этот контейнер предъявляется юзеру в UI, и только юзер отвечает за создание и удаление экземпляров. Напр юзер может создать Trigger неиспользуемый никогда, и наоборот, удалить используемый.

Другой класс Node просто использует назначенный ему экземпляр Trigger вызывая его методы (клиент). Разумеется нужно отследить удаление используемого с возможным undo, а также (де)сериализацию Node. Пока нет др классов-клиентов Trigger, но их появление возможно.

Как бы Вы это решали?

Спасибо
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #1 : Май 09, 2016, 11:29 »

У каждого триггера свой уникальный id (uint64_t). Нода хранит этот id. При необходимости взаимодействия с объектом триггер, нода получает указатель на него по сохраненному id. Если возвращается nullptr, то триггер удален пользователем. id удаленного триггера нода продолжает помнить, т.е. триггер может вернуться по undo. При сохранении нод можно проверить наличие триггера и сохранить не старый id, а 0 (для ноды не установлен триггер).
Записан
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #2 : Май 09, 2016, 11:42 »

Класс-менеджер триггеров (это Ваш контейнер) должен хранить мапу <id, *CTrigger>.
id - зависит от того, как хотите триггеры идентифицировать (число, строка и т.д.)
При желании при удалении триггера он может испускать сигнал "я помер".

Получить триггер - что то типа такого:

CTrigger* triggerPtr = TriggerManager::GetTrigger(auto id);
if (triggerPtr)
  // триггер жив
else
  // не было такого

« Последнее редактирование: Май 09, 2016, 11:45 от Racheengel » Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #3 : Май 09, 2016, 12:14 »

У каждого триггера свой уникальный id (uint64_t).
Да. Причем я начал просматривать др (смежные) классы - и тоже использование id смотрится резонно.

CTrigger* triggerPtr = TriggerManager::GetTrigger(auto id);
Так примерно и есть для 2 уже имеющихся реализаций, вот я и хочу обобщиться чтобы не налипал повторяющийся код xxxManager.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Май 10, 2016, 09:50 »

Обдумываю..

- стоит ли хранить ID в самом классе (напр Trigger) или пусть оно будет чисто ключом контейнера? (думаю последнее).

- как назначать "очередное" ID? Счетчик? (не уверен)

- стоит ли прилагать усилия чтобы контейнер (и только он) мог создавать/удалять экземпляры? (не уверен)

- оставлять ли в мапе "пустые" слоты (id + null)? (думаю да). Нужен ли метод(ы) для чистки пустых слотов?

- что делать с "append" (слияние 2 контейнеров)? Пока только одна такая операция и можно пробежаться по использующим (заткнуть эту дыру). Стоит ли замахиваться на большее?
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #5 : Май 10, 2016, 10:54 »

- стоит ли хранить ID в самом классе (напр Trigger) или пусть оно будет чисто ключом контейнера? (думаю последнее).
Он как бы не нужен самому триггеру, но бывает так, что функции/методу которому передается указатель на триггер, необходимо узнать его id.
Обойти это можно передавая таким функциям кроме указателя и его текущий id.

- как назначать "очередное" ID? Счетчик? (не уверен)
максимальный id триггера в контейнере +1

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

- оставлять ли в мапе "пустые" слоты (id + null)? (думаю да). Нужен ли метод(ы) для чистки пустых слотов?
Да пусть будут. Объект триггера все равно не будет разрушаться, а будет перемещаться в undo-стек и легко может вернуться обратно.
И методы для очистки не нужны. Человек все равно не сможет настолько изуродовать map частыми добавлениями-удалениями.

- что делать с "append" (слияние 2 контейнеров)? Пока только одна такая операция и можно пробежаться по использующим (заткнуть эту дыру). Стоит ли замахиваться на большее?
Если такая операция есть, придется реализовать. Я бы ее скорее всего сделал внешней утилитой.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Май 10, 2016, 11:38 »

максимальный id триггера в контейнере +1
Т.к. ID ключ, то ID последнего +1, но тогда я беру обязательство не удалять пустые слоты (или по крайней мере последний). Ничего плохого в этом пока не вижу, вот насколько оно выгодно. 

Да пусть будут. Объект триггера все равно не будет разрушаться, а будет перемещаться в undo-стек и легко может вернуться обратно.
Обязан (требование проекта) поддерживать "честное" удаление, undo пишется на диск. При этом пустые слоты будут потеряны после undo. Конечно хочется использовать методы записи/чтения также и для undo.

Если такая операция есть, придется реализовать. Я бы ее скорее всего сделал внешней утилитой.
ID 2 контейнеров слить/объединить не вопрос, но что делать с использующими 2-й (чьи ID изменились)? Тут можно размахнуться намного ширше: хранить не только "личное" ID, но и ID(s) кто ссылается на него + на кого он сам. Это дает интересные возможности, но может оказаться слишком большим/неадекватным.
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #7 : Май 10, 2016, 11:51 »

Т.к. ID ключ, то ID последнего +1
Ну если триггеры отсортированы по id (как в map), то последнего +1 

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

Обязан (требование проекта) поддерживать "честное" удаление, undo пишется на диск. При этом пустые слоты будут потеряны после undo. Конечно хочется использовать методы записи/чтения также и для undo.
Ну пусть храниться на диске, по клику мышки он в любой момент может стать объектом.

ID 2 контейнеров слить/объединить не вопрос, но что делать с использующими 2-й (чьи ID изменились)? Тут можно размахнуться намного ширше: хранить не только "личное" ID, но и ID(s) кто ссылается на него + на кого он сам. Это дает интересные возможности, но может оказаться слишком большим/неадекватным.
Я бы размахивался. Улыбающийся
Записан
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #8 : Май 17, 2016, 00:38 »

Добрый день

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

Есть довольно развесистый класс Trigger все экземпляры которого хранятся в глобальном контейнере. Этот контейнер предъявляется юзеру в UI, и только юзер отвечает за создание и удаление экземпляров. Напр юзер может создать Trigger неиспользуемый никогда, и наоборот, удалить используемый.

Другой класс Node просто использует назначенный ему экземпляр Trigger вызывая его методы (клиент). Разумеется нужно отследить удаление используемого с возможным undo, а также (де)сериализацию Node. Пока нет др классов-клиентов Trigger, но их появление возможно.

Как бы Вы это решали?

Спасибо

std::shared_ptr/ std::weak_ptr
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #9 : Май 17, 2016, 10:14 »

std::shared_ptr/ std::weak_ptr
Это решает лишь одну задачу - облегчается удаление Node Trigger.
« Последнее редактирование: Май 18, 2016, 09:31 от Igors » Записан
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #10 : Май 18, 2016, 01:25 »

std::shared_ptr/ std::weak_ptr
Это решает лишь одну задачу - облегчается удаление Node.

это решает проблему: "кто о ком знает, и кто кем владеет".
в частности - проблему времени жизни Trigger

Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #11 : Май 18, 2016, 10:08 »

это решает проблему: "кто о ком знает, и кто кем владеет".
Думаю это было столь же ясным/решенным и без всяких вумных указателей  Улыбающийся

Реализовал контейнер по ID, ну пока еще плотно его не использовал (пока надо дописать UI и все такое). Интересно обсудить:

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

- свободные "слоты" - все-таки решил удалять (кроме последнего)

- вот интересно хранение/восстановление "связок по ID". Пока использую примитивно, напр Node хранит ID Trigger'а. Теперь при его удалении не надо бегать обнулять указатель на него в Node. При undo и (де)сериализации также все складывается хорошо. Прикидывал как сделать хранение ID "внешним". Хранить Node в контейнере ID - явно в масть, экземпляры Node могут ссылаться друг на друга. Но дальше неясно. Напр как Node должен получать указатель на Trigger (без члена mTriggerID)?
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


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