Название: Наследование template Отправлено: Igors от Май 10, 2016, 13:51 Добрый день
Название темы корявое (не нашел лучшего), пример: вот сейчас я рисую некий template Код Первые 2 метода универсальны (не завязаны на др классы), а следующие 2 не очень - совсем необязательно что и в др проекте будет MyStream. Может в данном случае лучше сделать внешние операторы << и >>, но это частное решение. А как в общем случае сделать аккуратнее? Спасибо Название: Re: Наследование template Отправлено: ssoft от Май 10, 2016, 14:48 В любом случае необходимо операции сериализации отделять от данных.
Т.е. такие структуры должны содержать информацию только о себе и методы для ее изменения. О способе использования (в т.ч. способе сериализации) данные ни чего знать не должны. Существует известная проблема, связанная с правами доступа к внутренним полям, но если использовать только public доступ, то таких проблем не возникнет. Т.о. методы Read и Write должны быть внешними по отношению к MyTemplateClass и могут быть реализованы, например, через специализацию шаблона Код
и т.д. Вместе с методами сериализации можно и операторы <<,>> реализовать. Код
или что-то еще, не принципиально. Название: Re: Наследование template Отправлено: Igors от Май 11, 2016, 08:15 В любом случае необходимо операции сериализации отделять от данных. Не раз слышал эту "официальную доктрину", но мне она представляется спорной. Да, это убирает "зависимость", но ценой полного развала инкапсуляции - кому как не самому объекту знать как себя сохранять? Давно хотел попробовать такой вариант Т.е. такие структуры должны содержать информацию только о себе и методы для ее изменения. О способе использования (в т.ч. способе сериализации) данные ни чего знать не должны. Код Что здесь плохого? Название: Re: Наследование template Отправлено: Racheengel от Май 11, 2016, 08:59 Мне тоже непонятна идея с разделением. Вернее, ее практический смысл.
Каким образом объект будет менять закрытые данные? И в чем преимущества подобного деления? Если непременно хочется писать в несколько разных форматов, то объект все равно должен уметь выгрузить свои данные и восстановить их. В общем случае он может сериализироваться в какой-либо intermediate формат (например, в QDaraStream), а уже внешний сериалайзер может делать с этими данными что угодно. Но непосредственно сериализация данных должна быть частью объекта. Название: Re: Наследование template Отправлено: m_ax от Май 11, 2016, 09:34 Цитировать Мне тоже непонятна идея с разделением. Вернее, ее практический смысл. Вот сравните:Код
И это: Код
Цитировать Каким образом объект будет менять закрытые данные? Объявляете операторы чтения, записи друзьями.Название: Re: Наследование template Отправлено: Igors от Май 11, 2016, 14:59 Вот сравните: Ну это можно пережить. Хужее если такКод
Код И выясняется что если методы записи/чтения Object1 и Object2 не совпадают, то обобщить (де) сериализацию контейнера не удастся, придется возюкаться с каждым. При этом операторы << и >> ничем не лучше, просто они традиционны/каноничны, поэтому лучше их придерживаться. Название: Re: Наследование template Отправлено: Igors от Май 11, 2016, 15:09 Давно хотел попробовать такой вариант Сегодня попробовал и остался недоволен. Все хорошо пока (де)сериализация сводится к << и >>, на что я собсно и рассчитывал. Но вот как только что-то другое.. Напр у меня данные часто пишутся: идентификатор + длина + сами данные. А значит нужно подсчитать длину (в памяти или по файлу). Помещая этот код в тело template-метода я фактически признаю что пишу в конкретный (бинарный) поток, прощай общность.Код
Название: Re: Наследование template Отправлено: m_ax от Май 11, 2016, 16:57 Цитировать Но вот как только что-то другое.. Напр у меня данные часто пишутся: идентификатор + длина + сами данные. А значит нужно подсчитать длину (в памяти или по файлу). Помещая этот код в тело template-метода я фактически признаю что пишу в конкретный (бинарный) поток, прощай общность. Поэтому специализируете serializer, как уже было предложено под конкретные форматы..Код
Название: Re: Наследование template Отправлено: Racheengel от Май 11, 2016, 18:13 Объявляете операторы чтения, записи друзьями. Но в любом случае придется расширять классы, чтобы сделать это. Почему бы сразу не встроить в них универсальную сериализацию в стримовый формат? Название: Re: Наследование template Отправлено: m_ax от Май 11, 2016, 18:43 Цитировать Но в любом случае придется расширять классы, чтобы сделать это. Вы что имеете в виду под расширением? Я имел ввиду как то так:Код
Цитировать Почему бы сразу не встроить в них универсальную сериализацию в стримовый формат? Потому что во-первых, в этом нет особого смысла, а во-вторых, например, как вы запишите в поток какой-нибудь контейнер?Я вот так вот: Код А вы? Будете цикл организовывать? Название: Re: Наследование template Отправлено: __Heaven__ от Май 12, 2016, 09:46 А каким образом реализация и объявления должны быть разбросаны по файлам?
Со структурой понятно, что она должна быть в MyStruct.h и MyStruct.cpp. А как быть с методами записи в потоки? Как их распределять так, чтобы сохранялась переносимость между проектами? Название: Re: Наследование template Отправлено: Racheengel от Май 12, 2016, 10:00 Потому что во-первых, в этом нет особого смысла, а во-вторых, например, как вы запишите в поток какой-нибудь контейнер? Ну, по сути, Вы делаете то же самое, но с помощью операторов :) Я имел в виду такой вариант: class MyClass { public: virtual void storeData(QDataStream& v) const; virtual void restoreData(QDataStream& v); ... } и далее так: void MyClass::storeData(QDataStream& v) const { v << myData1 << myData2; } void MyClass::restoreData(QDataStream& v) { v >> myData1; v >> myData2; } а уже внешний контроллер может делать с v все, что захочет: MyClass c; ... QByteArray data; QDataStream v(&data); c.storeData(v); QSettings set(...); set.setValue("MyClassData", v); ... Название: Re: Наследование template Отправлено: m_ax от Май 12, 2016, 10:52 Цитировать А каким образом реализация и объявления должны быть разбросаны по файлам? Как решите, так и будет) Если друзья шаблонные, то обычно в том же хедере и реализация.Цитировать Ну, по сути, Вы делаете то же самое, но с помощью операторов Конечно) Только я всё равно не вижу чем лучше функции члены для чтения записи, чем специально отведённые для этого операторы? Когда я имею дело с каким-либо классом я в первую очередь буду ожидать от него следующего стандартного поведения (когда речь идёт о чтении/записи) Код
а не искать у него методов типа Write/storeData и т.д.. :) Название: Re: Наследование template Отправлено: Igors от Май 12, 2016, 11:01 Поэтому специализируете serializer, как уже было предложено под конкретные форматы.. Ну а смысл этой городушки? Напр в роли Stream тот же QDataStream, методов read/write у него нет. Код
Я имел в виду такой вариант: Это сериализация для одного конкретного потока QDataStream, и этот код пролетает в проекте без Qtclass MyClass { public: virtual void storeData(QDataStream& v) const; virtual void restoreData(QDataStream& v); ... } и далее так: void MyClass::storeData(QDataStream& v) const { v << myData1 << myData2; } как вы запишите в поток какой-нибудь контейнер? А за Вас кто это будет делать? Вообще цепочка операторов << и >> это самый низкий уровень когда данные следуют в известном порядке. А хотя бы уровнем выше их уже надо идентифицировать, как Вы будете это делать?Я вот так вот: Код
А вы? Будете цикл организовывать? Название: Re: Наследование template Отправлено: m_ax от Май 12, 2016, 11:49 Цитировать А за Вас кто это будет делать? Да это уже сделано. Посмотрите на QVector, например. Теперь чтоб его вместо одного движения записать в поток, придётся для каждого элемента вызывать не стандартные методы. Зачем? Цитировать А хотя бы уровнем выше их уже надо идентифицировать, как Вы будете это делать? А какое это имеет отношение к вопросу об использовании операторов<<>> vs мемберов? Название: Re: Наследование template Отправлено: Racheengel от Май 12, 2016, 12:12 Когда я имею дело с каким-либо классом я в первую очередь буду ожидать от него следующего стандартного поведения (когда речь идёт о чтении/записи) Код
а не искать у него методов типа Write/storeData и т.д.. :) Что значит "стандартное" поведение? В любом случае Вам придется для каждого поддерживаемого формата перегружать операторы. Сериализация в QDataStream будет отличаться от std::stream, таким образом, в любом случае obj должен явно поддерживать оба контейнера. Поэтому особых преимуществ, честно говоря, не вижу. Что еще важно - как по мне, если методы определены явно в классе - это повышает читабельность, т.к. человек сразу видит его интерфейс, и то, с чем "умеет" работать класс. В этих же методах явно определяется порядок сериализации и десериализации. А наличие внешних сериалайзеров и их дополнительные специализации - это вводит дополнительные сущности и в итоге усложняет понимание системы классов. Название: Re: Наследование template Отправлено: m_ax от Май 12, 2016, 12:45 Цитировать Что значит "стандартное" поведение? Я вот о чём:Код Я выбираю первый вариант. Название: Re: Наследование template Отправлено: Igors от Май 12, 2016, 13:07 Я вот о чём: Какой Вы быстрый :) А что бум делать если первый вариант вдруг не устраивает? См напр здесь (http://www.prog.org.ru/index.php?topic=29894.msg219900#msg219900). Код Я выбираю первый вариант. Наивно полагать что нарисовав << и >> мы уже решили все проблемы :) Это даже не пол-дела. В действительности поток I/O должен иметь какую-то организацию, а не просто "идущие подряд данные" с которыми потом хрен разберешься. В любом случае Вам придется для каждого поддерживаемого формата перегружать операторы. Сериализация в QDataStream будет отличаться от std::stream, Да, именно. Ну может пример с бинарной/текстовой сериализацией проще и выразительнее. В первом случае нужно писать ID и длины, во втором нужны имена и правила разбивки на строкиЧто еще важно - как по мне, если методы определены явно в классе - это повышает читабельность, т.к. человек сразу видит его интерфейс, и то, с чем "умеет" работать класс. В этих же методах явно определяется порядок сериализации и десериализации. А наличие внешних сериалайзеров и их дополнительные специализации - это вводит дополнительные сущности и в итоге усложняет понимание системы классов. Существенно усложняет - но другого не видно, иначе никак не изолировать инклуды ненужных классов.Название: Re: Наследование template Отправлено: Racheengel от Май 12, 2016, 14:37 Я выбираю первый вариант. Но это как раз и означает, что операторы >> << должны быть определены в контексте MyClass, т.к. сериализация QVector<T> явно требует этого. И для этого ВНЕШНИЕ классы в принципе не нужны. Название: Re: Наследование template Отправлено: m_ax от Май 12, 2016, 16:05 Цитировать Но это как раз и означает, что операторы >> << должны быть определены в контексте MyClass, т.к. сериализация QVector<T> явно требует этого Да, и я реализую их вместо мемберов write/read. Название: Re: Наследование template Отправлено: ssoft от Май 12, 2016, 17:47 Могу рассказать, как у нас решена задача сериализации в общем виде, причем с поддержкой версий структур данных для возможной бинарной совместимости.
Методы сериализации реализуются с помощью специализаций шаблонов Код
Для простоты объяснения понятие версии опущу. По умолчанию реализация методов выглядит так Код
По умолчанию, Loader и Saver вызывают один и тот же метод Serializer::invoke, и если методы сериализации и десериализации ни чем не отличаются (как это обычно и бывает), то достаточно специализировать только Serializer. Если отличия существуют из-за геттеров и сеттеров, например, то специализировать придется и Loader, и Saver (Serializer специализировать в этом случае не требуется). Предположим, что нам необходимо сериализовать структуру Код
Для этого необходимо специализировать один единственный метод сериализации Код
Таким образом, один метод реализует и сериализацию и десериализацию. Но как и куда? Здесь определяющую роль играет понятие архива. Архив - это ваш произвольный класс реализующий интерфейс для сериализации Код
или для десериализации Код
Куда/откуда и как происходит сохранение/чтение параметров решает разработчик архива. Архив может сериализовать в std::stream, в QDataStream, в QTextStream, в разных форматах - XML, JSON, BIN и т.п. и т.д. Для сериализации данных контейнеров типа std::vector, QVector для этих типов так же необходимо реализовать специализацию Serializer. Для наглядности - пример архива Код
Доступ к приватным членам, нужен не часто, при правильной реализации должно хватать API класса. Но если этого нет, можно использовать friend зависимости. Название: Re: Наследование template Отправлено: m_ax от Май 12, 2016, 17:57 Т.е. это фактически boost'овский вариант сериализации? Или есть принципиальные отличия?
Название: Re: Наследование template Отправлено: Igors от Май 13, 2016, 06:12 Могу рассказать, как у нас решена задача сериализации в общем виде, причем с поддержкой версий структур данных для возможной бинарной совместимости. Да, обобщить "туды-сюды" у меня руки так и не дошли. Интересно, спасибоТакой момент: вот "простейший пример" Код И типа "ну вот и вся сериализация" (одним движением). Однако это предполагает жесткий, неизменный порядок следования данных, все они должны быть прочитаны. Также ничего не известно о типе данных, мы должны полагаться что a (b, c) - именно данные тех самых типов что мы хотим. Поэтому так работает хорошо для относительно простых, базовых структур. В более сложных случаях данные должны быть идентифицированы и могут быть сериализованы в различном порядке. Как Вы решаете эту проблему? Название: Re: Наследование template Отправлено: ssoft от Май 13, 2016, 08:19 Т.е. это фактически boost'овский вариант сериализации? Или есть принципиальные отличия? Да, вариант очень похож на boost). Отличия существуют в ведении версий структур данных. У нас версия сериализации также является специализируемым параметром шаблона, а не входящим параметром в метод сериализации, как в boost. И по-умолчанию, используется самая последняя версия сериализации, а не 0, как в boost. Есть еще непринципиальные отличия - названия методов, использование операторов, но базовые концепции идентичны, а решения очень похожи. Код И типа "ну вот и вся сериализация" (одним движением). Однако это предполагает жесткий, неизменный порядок следования данных, все они должны быть прочитаны. Также ничего не известно о типе данных, мы должны полагаться что a (b, c) - именно данные тех самых типов что мы хотим. Поэтому так работает хорошо для относительно простых, базовых структур. В более сложных случаях данные должны быть идентифицированы и могут быть сериализованы в различном порядке. Как Вы решаете эту проблему? 1. Кроме линейной/последовательной сериализации, таким же образом решена задача параметрической сериализации. Код
Выдержка из документации: Цитировать Механизмы сериализации можно разделить на: * линейные * параметрические Линейный механизм сериализации подразумевает под собой последовательный порядок сохранения и восстановления данных. Другими словами, чтобы получить пятую запись необходимо восстановить и все четыре предыдущие. Параметрический механизм позволяет получить записи по уникальному параметру - ключу. Таким образом та же 5-я запись может быть получена из десериализованных данных сразу, минуя предыдущие четыре. Преимуществом линейного способа сериализации является максимальная скорость сохранения/восстановления данных в их полном виде, так как в параметрической форме при восстановлении требуется осуществлять поиск необходимой записи по параметрическому ключу. Тем не менее, в параметрическом виде можно сериализовывать не все записи, а только часть, и иногда осуществлять сериализацию/десериализацию между разными типами. 2. Для понимания, какой тип сохраняется и какая версия метода сериализации используется, эту информацию можно записать в архив. У нас архив устроен так, что сериализация/десериалзация данных происходит пакетами QByteArray. Зачитываем пакет, из пакета читаем тип данных и версию сериализации, если зачитать не смогли, то пакет может быть проигнорирован, или особо обработан, или процесс десериализации прерван. Я не описал, как ведется версионность, но это оочень длинный пост получается. К слову сказать, чтобы не заниматься явной специализацией, предусмотрены макросы, упрощающие запись Код
Название: Re: Наследование template Отправлено: Igors от Май 15, 2016, 11:27 Цитировать Параметрический механизм позволяет получить записи по уникальному параметру - ключу. Таким образом та же 5-я запись может быть получена из десериализованных данных сразу, минуя предыдущие четыре. Код Таких случаев хоть отбавляй. Создавать новую "версию потока" на каждое изменение - ну не знаю, выглядит накладно. Но все равно, основательно у Вас в конторе сделано. Спасибо Название: Re: Наследование template Отправлено: Racheengel от Май 15, 2016, 12:49 У нас десериализация работает в 2 этапа:
1. Выгребаются ВСЕ сохраненные параметры в двоичном виде в формате ключ.длина.значения и пакуются в мэп. 2. А уже из мэпа достаются нужные параметры по ключам. В итоге и очередность не важна, и контент можно конвертировать из предыдущих версий. Версия архива пишется в хэдер, т.к. она для всех данных едина. Название: Re: Наследование template Отправлено: ssoft от Май 15, 2016, 18:52 Может не самый лучший пример - не припомню когда мне была нужна только 5-я запись :) Здесь имеется ввиду, что при изменении версии структуры, ее новые данные сериализуются по новым ключам, а удаленные можно не сериализовать совсем. Тогда старое ПО легко может частично десериализовать новую структуру и не "сломается". В линейном случае так легко это не прокатит. Хорошим примером такого подхода является protobuf. Создавать новую "версию потока" на каждое изменение - ну не знаю, выглядит накладно. Здесь не требуется создавать новую версию потока (архива), требуется изменить только метода сериализации для структуры, а версию структуры можно сохранять в сам архив. Тогда при чтении по версии можно определить, может ли структура быть десериализована и каким способом. У нас версию имеет не архив, а сама структура. Это может показаться накладно, однако такой подход обеспечивает бинарную совместимость между различными версиями ПО. Это важно, например, когда ПО состоит из множества разных процессов, взаимодействующих между собой, а при обновлении система не может быть остановлена. У нас десериализация работает в 2 этапа: 1. Выгребаются ВСЕ сохраненные параметры в двоичном виде в формате ключ.длина.значения и пакуются в мэп. 2. А уже из мэпа достаются нужные параметры по ключам. ... Это и есть параметризация, когда данные пакуются по ключам. Название: Re: Наследование template Отправлено: _Bers от Май 17, 2016, 00:36 Добрый день Название темы корявое (не нашел лучшего), пример: вот сейчас я рисую некий template Код Первые 2 метода универсальны (не завязаны на др классы), а следующие 2 не очень - совсем необязательно что и в др проекте будет MyStream. Может в данном случае лучше сделать внешние операторы << и >>, но это частное решение. А как в общем случае сделать аккуратнее? Спасибо это и есть общее и общепринятое решение. |