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

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

Страниц: [1] 2 3   Вниз
  Печать  
Автор Тема: Sets of Objects  (Прочитано 20289 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« : Сентябрь 10, 2009, 17:50 »

Добрый день

Вопрос по "проектированию", как организовать данные

Есть массив (список, вектор) разнородных объектов. Пользователь может создавать/удалять/редактировать произвольные "подмножества" (sets) объектов из общего списка. Set может быть пустым, один объект может входить в любое число sets. Сам объект в set копироваться не должен, set должен обеспечивать только доступ к нему (например через указатель).

Вопрос: как сохранить set в файл и прочитать из файла? Или более общий вопрос: как организовать данные для таких sets?

Записан
spectre71
Гость
« Ответ #1 : Сентябрь 10, 2009, 18:02 »

Сохранить отдельный set или исходное множество со всеми прожденными sets?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #2 : Сентябрь 10, 2009, 18:22 »

Сохранить отдельный set или исходное множество со всеми прожденными sets?
Само собой, исходное множество объектов сохраняется. Но кроме этого нужно сохранить все sets созданные пользователем (без повторного сохранения самих объектов). Принцип: set указывает/ссылается на данные (а не копирует их)
Записан
spectre71
Гость
« Ответ #3 : Сентябрь 10, 2009, 18:34 »

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

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Сентябрь 10, 2009, 18:52 »

У меня в проекте решается такая задача.
Принцип прост. Сохраняешь(сериализуешь) объекты для основного множества и ссылки(идентификаторы) на них для sets.
Если нужны подробности, задавай вопросы, я на этом уже "съел стайку собак" Улыбающийся
Хорошо, тогда начнем с первой с первой собаки Улыбающийся Как "сериализовать" объекты и что то за идентификаторы (ID)? Как/когда Вы их создаете?
Записан
spectre71
Гость
« Ответ #5 : Сентябрь 10, 2009, 19:25 »

Хорошо, тогда начнем с первой с первой собаки Улыбающийся Как "сериализовать" объекты и что то за идентификаторы (ID)? Как/когда Вы их создаете?
Это уже 2 или 3 собаки Улыбающийся
1) Сериализуем простые типы данных:
   byte
   integer разного размера
   real
   boolean
   байтовый массив определенной длины
2) Сериализуем ссылки. Ссылка это идентифакатор(у меня числовой) на сериализуемый  объект
3) Сериализуем объекты. У меня все сериализуемые объекты имеют общего предка, но возможны и другие подходы.
  Объект сериализует свои поля(не обязательно все), различаются:
  - простые типы данных (1)
  - объекты (3)
  - ссылки на объекты (2)
  - NULL
Возможен подход когда не различаются объект и его ссылка, у меня так было в начале.
  Но,
    - это труднее реализовать
    - труднее сохранять, требуется дополнительная обработка на то, что сохранен ли уже объект, если не сохранен, то сериализуем как объект, если сохранен, то сериализуем как ссылку.
    - труднее восстанавливать, требуется дополнительная обработка на то, что создан ли уже объект.
    - черевато большим кол-вом ошибок.
Поэтому, лучше различать объекты и ссылки на них.
  - Объекты - там где создаются/удаляются
  - Ссылки - где сохраняются указатели на созданные объекты
4) Сериализуемые объекты различаются на 2 типа
  - создаваемый - объект создается при чтении, может быть только указателем и до чтения == NULL
  - запоняемый - до чтения объект уже создан(например в конструкторе другого объекта либо автоматически), необходимо только заполнить(прочитать) его поля
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Сентябрь 10, 2009, 19:51 »

Спасибо за подробный ответ. Мне ясно что Вы сохраняете(читаете) в потоке ТИП объектов/данных, это неизбежно так или иначе. Но неясно как Вы поддерживаете связки при записи/чтении. Давайте на простом примере:

Есть 3 объекта, например QString. Пользователь создал set в который входят 2-я и 3-я строка. Вопросы:

Как организовать данные самого set?
Что и в каком порядке будет писАться/читаться?

Записан
BRE
Гость
« Ответ #7 : Сентябрь 10, 2009, 20:10 »

Есть 3 объекта, например QString. Пользователь создал set в который входят 2-я и 3-я строка. Вопросы:

Как организовать данные самого set?
Что и в каком порядке будет писАться/читаться?
Напишу свои мысли по этому вопросу:
Есть пул указателей на объекты, пусть для этого используется QList<Base*> pool;
В нем содержаться все возможные объекты. При создании нового объекта он сохраняется в пуле. Пул является владельцем объектов, т.е. при уничтожении пула будут уничтожены и сами объекты. Набор не владеет объектами, т.е. при его разрушении уничтожается сам набор, а объекты не трогаются.

pool.append( new Str( "String # 1" ) );
pool.append( new Str( "String # 2" ) );
pool.append( new Str( "String # 3" ) );

// В наборе set1 хранятся указатели на 2 и 3 строку
QList<Base*> set1;
set1.append( m_pool[ 1 ] );
set1.append( m_pool[ 2 ] );

// В наборе set2 хранятся указатели на 1 и 3 строку
QList<Base*> set1;
set1.append( m_pool[ 0 ] );
set1.append( m_pool[ 2 ] );

При хранении в памяти в качестве уникального ключа используется адрес размещения объекта. При хранении на диске в качестве ключа используется порядковый номер сохраненного объекта.

Для сохранения:
* Сохраняем последовательно все объекты из пула, в том порядке в котором они лежат.
* Сохраняем наборы

Структурный вид файла:
[количество объектов в пуле]
[объект # 0] // String # 1
[объект # 1] // String # 2
[объект # 2] // String # 3
[количество наборов]
[Набор 1]
  [int: 1] // индекс объекта в пуле
  [int: 2]
[Набор 2]
  [int: 0]
  [int: 2]
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #8 : Сентябрь 10, 2009, 20:28 »

При хранении в памяти в качестве уникального ключа используется адрес размещения объекта. При хранении на диске в качестве ключа используется порядковый номер сохраненного объекта.
Да вот именно так оно и сделано Улыбающийся И хотя это работает уже много лет - это реально "плохо" (очень мягко говоря). Проблемы лезут из всех щелей в такой схеме, деваться некуда - нужно переделывать. Пока точно не знаю как, давайте людей послушаем
Записан
spectre71
Гость
« Ответ #9 : Сентябрь 10, 2009, 20:31 »

Правильно, спасибо BRE! Принцип именно этот, но реализация может быть разная.
Ссылаюсь на конкретную, мою, реализацию.
У меня:
1) Сериализуемый класс наследуется от общего предка RW
2) Сериализуемый класс(за исключением абстрактных предков) должен имееть макро вставку(аналогично Q_OBJECT в QT) для мета-объектной идентификации(нужна при чтении/восстановлении объектов из потока), но с именем класса, примерно так:
Код:
class TFileID : public RW {
    SBW_CLASS_ID(TFileID)
...
...
}
Плюс к этому еще кое-что, прописывается вне классов, но тоже очень простое(я обхожусь без "MOC" механизмов реализованных в QT, которые требуют обработку перед компиляцией)
3) Каждый сохраняемый(реальный объект) перед сохранением получат уникальный ID(в моем случае числовой), который записывается в поток вместе с данными объекта
4) При сохранении объектов в некий список прописывается пара - адрес(указатель) объекта + его ID
5) При сохранении ссылки находим в таблице по адресу(указателю) его ID и соответственно(неким образом) сохраняем в потоке
6) Подобно при чтении
Записан
BRE
Гость
« Ответ #10 : Сентябрь 10, 2009, 20:34 »

Да вот именно так оно и сделано Улыбающийся И хотя это работает уже много лет - это реально "плохо" (очень мягко говоря). Проблемы лезут из всех щелей в такой схеме, деваться некуда - нужно переделывать. Пока точно не знаю как, давайте людей послушаем
Так а что за проблемы, рассказывай. Давайте вместе подумаем.
В этой системе нужно точно распределить кто может создавать объекты, кто их должен удалять, как их регистрировать в пуле и т.д.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #11 : Сентябрь 10, 2009, 20:52 »

Так а что за проблемы, рассказывай. Давайте вместе подумаем.
О проблемах тут можно целый роман написать Улыбающийся Рассмотрим пару простеньких

1) Пользователь удаляет объект. Нужно пробежаться по всем set'ам и удалить указатель на удаляемый объект из каждого set'а. Как Вы знаете, я не фанат ООП, но это уж слишком даже для меня. Это как же получается, ВСЕ объекты должны знать о существовании set'ов?

2) Пользователь передумал и "возвращает объект взад" вызывая undo. Как поддержать откат? Какие данные заносить в стек undo? Давайте сохраним для undo все set'ы в которые объект входил? Не жирно ли?
Записан
spectre71
Гость
« Ответ #12 : Сентябрь 10, 2009, 20:53 »

Далее(у меня).
Каждый неабстрактный класс унаследованный от RW должен определить для сериализации всего один виртуальный метод, пример:
Код:
void TFileID::reg(RWControl* control) {
// ParentForTFileID(control); // if necessary
  SBW_REG_DATA (File);
  SBW_REG_DATA (Hash);
  SBW_REG_INT (Size);
  SBW_REG_DATA (MTime);
}
Ее одной достаточно и для записи и для чтения!

Для сериализации  полей у меня определены следующие макросы:
SBW_REG_DATA(__value) {...} // для сериализации объектов
SBW_REG_BOOL(__value) {...} // для сериализации boolean(bit)
SBW_REG_INT(__value)  {...} // для сериализации integer от 8 до 64 битных
SBW_REG_ENUM(__value) {...} // для сериализации enum {}
SBW_REG_REAL(__value) {...} // для сериализации double
SBW_REG_OBJ(__value)  {...} // для сериализации создаваемых объектов
SBW_REG_REF(__value)  {...} // для сериализации ссылок на объекты
SBW_REG_BUFF(__value, __len) {...} // для сериализации буфера(массива байт)

Записан
spectre71
Гость
« Ответ #13 : Сентябрь 10, 2009, 21:04 »

1) Пользователь удаляет объект. Нужно пробежаться по всем set'ам и удалить указатель на удаляемый объект из каждого set'а. Как Вы знаете, я не фанат ООП, но это уж слишком даже для меня. Это как же получается, ВСЕ объекты должны знать о существовании set'ов?

2) Пользователь передумал и "возвращает объект взад" вызывая undo. Как поддержать откат? Какие данные заносить в стек undo? Давайте сохраним для undo все set'ы в которые объект входил? Не жирно ли?
Это никак не связано с записью/чтением(сериализацией) системы объектов в поток(например, запись в файл и восстановление из файла; или создание копии)!
Это совсем другая задача, вернее задачи.
Записан
BRE
Гость
« Ответ #14 : Сентябрь 10, 2009, 21:05 »

1) Пользователь удаляет объект. Нужно пробежаться по всем set'ам и удалить указатель на удаляемый объект из каждого set'а. Как Вы знаете, я не фанат ООП, но это уж слишком даже для меня. Это как же получается, ВСЕ объекты должны знать о существовании set'ов?
Есть пул объектов (ObjPool).
Есть пул наборов (SetPool).

При создании набора (в конструкторе) он регистрируется в пуле наборов.

При создании объекта (в конструкторе) он регистрируется в пуле объектов, в деструкторе он:
* просит объект SetPool вычеркнуть его из всех наборов;
* просит объект ObjPool вычеркнуть его из пула объектов.

2) Пользователь передумал и "возвращает объект взад" вызывая undo. Как поддержать откат? Какие данные заносить в стек undo? Давайте сохраним для undo все set'ы в которые объект входил? Не жирно ли?
Возвращает объект из набора - удалили указатель на объект из набора.
Записан
Страниц: [1] 2 3   Вверх
  Печать  
 
Перейти в:  


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