Программа должна сохранять некоторую информацию в собственном формате. По мере развития софта, код будет меняться, набор сохраняемых данных (собственно формат файла) соответственно тоже будет меняться.
Требуется обеспечить в будущих версиях поддержку старых форматов файла. Пользователь должен иметь возможность создать файл «старой версии» отказавших от некоторых плюшек неподдерживаемых старой версией. И, самое главное, пользователь должен иметь возможность прочитать «старую версию» файла новой версией программы.
Сейчас дело дошло до классов читателя и писателя – требуется аналогичное разделение.
Появилось подозрение, что в итоге половина кода будет иметь такое разделение.
Короче.. Как правильно организовать поддержку формата файла старой версии? Есть какие-нибудь паттерны на эту тему? Или мой подход не совсем ужасен?
В общем, любые идеи будут интересны.
Спасибо.
Здравствуйте, rsdh, Вы писали:
R>Короче.. Как правильно организовать поддержку формата файла старой версии? Есть какие-нибудь паттерны на эту тему? Или мой подход не совсем ужасен? R>В общем, любые идеи будут интересны. R>Спасибо.
А сколько данных? Например, если сохранять в xml — можно безболезненно добавлять новые поля. Есть более компактный и шустрый аналог — protobuf. Еще можно на boost serialization посмотреть.
Здравствуйте, enji, Вы писали:
E>Здравствуйте, rsdh, Вы писали:
E>А сколько данных? Например, если сохранять в xml — можно безболезненно добавлять новые поля. Есть более компактный и шустрый аналог — protobuf. Еще можно на boost serialization посмотреть.
Данных много, гигабайт наберется. protobuf смотрел, пользовал, но тут не тот случай. Структура файла скорее похожа на образ, есть управляющие данные описывающие расположение в файле полезной информации, а есть собственно сама полезная информация (в больших объемах). Структуры в исходном сообщении по идее должны описывать расположение (смещение/размер) полезной информации в файле.
Вопрос на самом деле не про сериализацию данных, а про разные версии кода, обрабатывающего эти данные.
Есть программа с некоторым функционалом умеющая писать какой-то файл.
Есть "новая" версия этой программы с расширенным функционалом. Код этой программы насквозь отличается мелочами от предыдущей версии. Как совместить в это программе "старую" и "новую" версию?
файл -- читатель -- ядро -- писатель -- файл
В исходном сообщении я написал как я разделил версии "файл". Как быть с читателем, писателем и ядром? Мой вариант — аналогично структурам, вынести все классы-обработчики данных, имеющие какое-либо отношение к функционалу порождающему отличия в версиях, в пространства имен и использовать так
Здравствуйте, rsdh, Вы писали:
R>В исходном сообщении я написал как я разделил версии "файл". Как быть с читателем, писателем и ядром? Мой вариант — аналогично структурам, вынести все классы-обработчики данных, имеющие какое-либо отношение к функционалу порождающему отличия в версиях, в пространства имен и использовать так
Возможно проще сделать конвертеры, понижающие\повышающие версию файла. Тогда основная программа всегда работает с самой новой версией, а при загрузке \ сохранении при необходимости запускается конвертер. Однако при гигабайте данных возможны тормоза. С другой стороны, конвертер запускается сравнительно редко.
Если в файле просто лежат массивы структур — можно описать содержимое структуры:
Здравствуйте, rsdh, Вы писали:
R>Данных много, гигабайт наберется. protobuf смотрел, пользовал, но тут не тот случай.
А кстати почему — не тот случай? Протобуф как раз и был изобретен для хранения большого числа не очень больших объектов... Кроме того, он закроет все проблемы с изменением версии компилятора, 32/64 и т.д.
Здравствуйте, enji, Вы писали:
E>Если в файле просто лежат массивы структур — можно описать содержимое структуры:
В составе структуры может быть поле с версией этой структуры. Так, например, некоторые WinAPI поступают (TAPI, RichText...).
Иногда версию структуры можно определить по её размеру, т.е. поле cbSize служит для этих целей.
Здравствуйте, enji, Вы писали:
E>А сколько данных? Например, если сохранять в xml — можно безболезненно добавлять новые поля.
Проблема не в полях — бинарный парсер тоже можно натаскать на пропуск неизвестных частей. Основная опа-опа начинается при изменении смысла сущности. Например, из линейной коллекции надо сделать дерево, из кортежа — коллекцию, из двух сущностей — три с половиной и т.п.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, enji, Вы писали: E>>Если в файле просто лежат массивы структур — можно описать содержимое структуры: К>В составе структуры может быть поле с версией этой структуры. Так, например, некоторые WinAPI поступают (TAPI, RichText...). К>Иногда версию структуры можно определить по её размеру, т.е. поле cbSize служит для этих целей.
Примерно так и сделал — в файле есть "главный" заголовок, который не должен меняться в будущих версиях, в нем информация о версии формата файла.
namespace Tool
{
namespace FileFormat
{
#pragma pack(push, 1)
struct MainHeader
{
BYTE bSignature[8]; // File signature
UINT64 nProgramVersion; // Program version file was created
UINT64 nMinProgVersion; // Minimum program version that can read file
UINT64 nFormatVersion; // Version of file format
UINT64 nNextOffset; // Infile absolute byte offset, where placed...
UINT64 nSecondDup; // Infile absolute byte offset, where placed second copy of this structure
// by default - last sizeof(MainHeader) bytes of a file
UINT64 nReserved1; // Reserved
UINT64 nReserved2; // Reserved
UINT64 nMagic; // Salt
UINT64 nSelfCRC; // CRC64 of this structure except this field
};
#pragma pack(pop)
};
};
Здравствуйте, enji, Вы писали:
E>Здравствуйте, rsdh, Вы писали:
R>>Данных много, гигабайт наберется. protobuf смотрел, пользовал, но тут не тот случай.
E>А кстати почему — не тот случай? Протобуф как раз и был изобретен для хранения большого числа не очень больших объектов... Кроме того, он закроет все проблемы с изменением версии компилятора, 32/64 и т.д.
Не тот случай, потому проблема не в сериализации — нет смысла использовать protobuf, там и без него все просто. Проблема в организации разных версий кода. В разных версиях программы будет разный код ядра обрабатывающий/формирующий данные файла и protobuf тут не поможет.
Во-первых, не пытаться предсказать будущее.
Во-вторых, вначале файле положить текстовое поле с номером версии.
Все. Остальное будете делать по мере изменения задачи.
Здравствуйте, Vzhyk, Вы писали:
V>22.05.2012 0:34, rsdh написал:
V>Во-первых, не пытаться предсказать будущее. V>Во-вторых, вначале файле положить текстовое поле с номером версии. V>Все. Остальное будете делать по мере изменения задачи.
Тут и предсказывать нечего. Формат точно изменится — все фичи с наскоку не реализовать, а добавляя их в будущем неизбежно придется менять формат.
Хочется подготовить архитектуру для этих изменений сейчас, когда это еще можно сделать.
Про версию писал выше
R>Программа должна сохранять некоторую информацию в собственном формате. По мере развития софта, код будет меняться, набор сохраняемых данных (собственно формат файла) соответственно тоже будет меняться. R>Требуется обеспечить в будущих версиях поддержку старых форматов файла. Пользователь должен иметь возможность создать файл «старой версии» отказавших от некоторых плюшек неподдерживаемых старой версией. И, самое главное, пользователь должен иметь возможность прочитать «старую версию» файла новой версией программы.
Я, наверное, туповат и чего-то недопонял в задаче. Потому что на первый взгляд все просто, как грабля.
class BaseFormat {
virtual void parse(...) /*= 0*/;
virtual void paramter1() /*= 0*/;
....
virtual ~BaseFormat();
};
class FormatVersion1 : public BaseFormat {
void parse(...);
virtual void parameterN();
...
};
BaseFormat* get_format(ifstream f) {
int version;
f >> version;
return get_format(version);
}
BaseFormat* get_format(int version) {
switch (version) {
case 1: return new FormatVersion1;
case 2: return new FormatVersion2;
}
throw"Unknown format version";
}
Здравствуйте, Vamp, Вы писали:
R>>Программа должна сохранять некоторую информацию в собственном формате. По мере развития софта, код будет меняться, набор сохраняемых данных (собственно формат файла) соответственно тоже будет меняться. R>>Требуется обеспечить в будущих версиях поддержку старых форматов файла. Пользователь должен иметь возможность создать файл «старой версии» отказавших от некоторых плюшек неподдерживаемых старой версией. И, самое главное, пользователь должен иметь возможность прочитать «старую версию» файла новой версией программы. V>Я, наверное, туповат и чего-то недопонял в задаче. Потому что на первый взгляд все просто, как грабля.
Скрытый текст
V>
V>class BaseFormat {
V> virtual void parse(...) /*= 0*/;
V> virtual void paramter1() /*= 0*/;
V>....
V> virtual ~BaseFormat();
V>};
V>class FormatVersion1 : public BaseFormat {
V> void parse(...);
V> virtual void parameterN();
V>...
V>};
V>BaseFormat* get_format(ifstream f) {
V> int version;
V> f >> version;
V> return get_format(version);
V>}
V>BaseFormat* get_format(int version) {
V> switch (version) {
V> case 1: return new FormatVersion1;
V> case 2: return new FormatVersion2;
V> }
V> throw"Unknown format version";
V>}
V>
V>Разве нет?
Да. Все так. Только там зависимость от формата идет чуть глубже, чем писатель/читатель.
Объясню с другой стороны. Добавляем в софт новую фичу. Теперь программа умеет бегать за пивом, появился класс СBuyBeer, изменилось ядро для работы с этим классом. Как результат — требуется в файл записать инфу о том, кто, куда, как, зачем и почему побежал. Завтра программа научится строить какие-нибудь графики, шифровать или архивировать сохраняемые данные и тд...
Т.е. меняется не только читатель и писатель, а все ядро. Нужно сохранить старый код для работы со старыми форматами, добавить новый для нового. Хотел разделить версии в разные пространства имен, здесь
22.05.2012 15:29, RSDHost написал:
> Тут и предсказывать нечего. Формат точно изменится — все фичи с наскоку > не реализовать, а добавляя их в будущем неизбежно придется менять формат. > Хочется подготовить архитектуру для этих изменений сейчас, когда это еще > можно сделать.
Я написал, исходя из моего опыта. Раньше я тоже пытался предугадывать
что-то, архитектуру продумывать. В итоге, я убедился, что единственный
гарантированно работающий вариант, что написал выше.
Ну и еще в качестве формата xml удобен оказался.
Все остальное лучше вначале не наворачивать вообще.
22.05.2012 16:39, Vzhyk написал:
> Ну и еще в качестве формата xml удобен оказался. > Все остальное лучше вначале не наворачивать вообще.
И еще отказался от структур в коде на С для хранения подобного.
Использую подобие xpath: value = get_value("/root/field1/field2",
def_value).
Соответственно с версиями в таком подходе проблем нет.
Здравствуйте, RSDHost, Вы писали:
RSD>Да. Все так. Только там зависимость от формата идет чуть глубже, чем писатель/читатель. RSD>Объясню с другой стороны. Добавляем в софт новую фичу. Теперь программа умеет бегать за пивом, появился класс СBuyBeer, изменилось ядро для работы с этим классом. Как результат — требуется в файл записать инфу о том, кто, куда, как, зачем и почему побежал. Завтра программа научится строить какие-нибудь графики, шифровать или архивировать сохраняемые данные и тд... RSD>Т.е. меняется не только читатель и писатель, а все ядро. Нужно сохранить старый код для работы со старыми форматами, добавить новый для нового. Хотел разделить версии в разные пространства имен, здесь
писал, но не уверен, что это правильно. Надеялся, что тут подскажут какой нибудь хитрый паттерн, дабы велосипед не городить.
Дык а зачем иметь в одной программе весь код? Надо ли вообще иметь возможность сохранять в старом формате или достаточно только нового?
Даже если нужна конвертация в обе стороны, ее можно оформить именно как конвертацию — сконвертировать форматы проще, чем поддерживать работу с обоими. При этом вся программа поддерживает только актуальный формат и написаны понижающие и повышающие конвертеры
Здравствуйте, enji, Вы писали:
E>Здравствуйте, RSDHost, Вы писали:
E>Дык а зачем иметь в одной программе весь код? Надо ли вообще иметь возможность сохранять в старом формате или достаточно только нового? E>Даже если нужна конвертация в обе стороны, ее можно оформить именно как конвертацию — сконвертировать форматы проще, чем поддерживать работу с обоими. При этом вся программа поддерживает только актуальный формат и написаны понижающие и повышающие конвертеры
Ну т.е. весь саппортный код переедет из ядра в конвертер? Чем это лучше? По факту в конвертере будет 80% кода ядра, потому что конвертация на понижение версии потребует удаления из файла данных неподдерживаемых старой версией, а чтобы их правильно удалить, нужно обработать инфу из файла, а это значит нужно тащить обрабатывающий код в конвертер. Это совсем никуда не годится.
В общем, всем спасибо. Останусь на исходном варианте — каждую версию в свое пространство имен, по версии файла использовать нужный набор классов.