Russian Qt Forum

Программирование => Общий => Тема начата: Igors от Май 09, 2016, 11:18



Название: Обобщить взаимодействие
Отправлено: Igors от Май 09, 2016, 11:18
Добрый день

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

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

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

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

Спасибо


Название: Re: Обобщить взаимодействие
Отправлено: Old от Май 09, 2016, 11:29
У каждого триггера свой уникальный id (uint64_t). Нода хранит этот id. При необходимости взаимодействия с объектом триггер, нода получает указатель на него по сохраненному id. Если возвращается nullptr, то триггер удален пользователем. id удаленного триггера нода продолжает помнить, т.е. триггер может вернуться по undo. При сохранении нод можно проверить наличие триггера и сохранить не старый id, а 0 (для ноды не установлен триггер).


Название: Re: Обобщить взаимодействие
Отправлено: Racheengel от Май 09, 2016, 11:42
Класс-менеджер триггеров (это Ваш контейнер) должен хранить мапу <id, *CTrigger>.
id - зависит от того, как хотите триггеры идентифицировать (число, строка и т.д.)
При желании при удалении триггера он может испускать сигнал "я помер".

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

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



Название: Re: Обобщить взаимодействие
Отправлено: Igors от Май 09, 2016, 12:14
У каждого триггера свой уникальный id (uint64_t).
Да. Причем я начал просматривать др (смежные) классы - и тоже использование id смотрится резонно.

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


Название: Re: Обобщить взаимодействие
Отправлено: Igors от Май 10, 2016, 09:50
Обдумываю..

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

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

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

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

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


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

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

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

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

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


Название: Re: Обобщить взаимодействие
Отправлено: Igors от Май 10, 2016, 11:38
максимальный id триггера в контейнере +1
Т.к. ID ключ, то ID последнего +1, но тогда я беру обязательство не удалять пустые слоты (или по крайней мере последний). Ничего плохого в этом пока не вижу, вот насколько оно выгодно. 

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

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


Название: Re: Обобщить взаимодействие
Отправлено: Old от Май 10, 2016, 11:51
Т.к. ID ключ, то ID последнего +1
Ну если триггеры отсортированы по id (как в map), то последнего +1 

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

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

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


Название: Re: Обобщить взаимодействие
Отправлено: _Bers от Май 17, 2016, 00:38
Добрый день

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

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

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

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

Спасибо

std::shared_ptr/ std::weak_ptr


Название: Re: Обобщить взаимодействие
Отправлено: Igors от Май 17, 2016, 10:14
std::shared_ptr/ std::weak_ptr
Это решает лишь одну задачу - облегчается удаление Node Trigger.


Название: Re: Обобщить взаимодействие
Отправлено: _Bers от Май 18, 2016, 01:25
std::shared_ptr/ std::weak_ptr
Это решает лишь одну задачу - облегчается удаление Node.

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



Название: Re: Обобщить взаимодействие
Отправлено: Igors от Май 18, 2016, 10:08
это решает проблему: "кто о ком знает, и кто кем владеет".
Думаю это было столь же ясным/решенным и без всяких вумных указателей  :)

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

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

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

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