Как поддерживать старый формат файла?
От: rsdh  
Дата: 21.05.12 21:34
Оценка:
Доброго времени суток

Программа должна сохранять некоторую информацию в собственном формате. По мере развития софта, код будет меняться, набор сохраняемых данных (собственно формат файла) соответственно тоже будет меняться.
Требуется обеспечить в будущих версиях поддержку старых форматов файла. Пользователь должен иметь возможность создать файл «старой версии» отказавших от некоторых плюшек неподдерживаемых старой версией. И, самое главное, пользователь должен иметь возможность прочитать «старую версию» файла новой версией программы.

Пока получается такая не хитрая картина


// fmtv0.h
namespace Tool
{
    namespace FileFormat
    {
        namespace Version0
        {
            const int Version = 0;

            struct SomeData0
            {
                char a;
            };
            struct SomeData1
            {
                char a;
            };
        };
    };
};



// fmtv1.h
#include <fmtv0.h>

namespace Tool
{
    namespace FileFormat
    {
        namespace Version1
        {
            const int Version = 1;

            typedef Version0::SomeData0     SomeData0;

            struct SomeData1
            {
                int b;
            };
            struct SomeData2
            {
                BYTE a[10];
            };
        };

    };
};



// fmt.h
#include <fmtv0.h>
#include <fmtv1.h>

namespace Tool
{
    namespace FileFormat
    {
        namespace CurrentVersion = Version0;
    };
};



Сейчас дело дошло до классов читателя и писателя – требуется аналогичное разделение.
Появилось подозрение, что в итоге половина кода будет иметь такое разделение.

Короче.. Как правильно организовать поддержку формата файла старой версии? Есть какие-нибудь паттерны на эту тему? Или мой подход не совсем ужасен?
В общем, любые идеи будут интересны.
Спасибо.
Re: Как поддерживать старый формат файла?
От: enji  
Дата: 21.05.12 21:54
Оценка:
Здравствуйте, rsdh, Вы писали:

R>Короче.. Как правильно организовать поддержку формата файла старой версии? Есть какие-нибудь паттерны на эту тему? Или мой подход не совсем ужасен?

R>В общем, любые идеи будут интересны.
R>Спасибо.

А сколько данных? Например, если сохранять в xml — можно безболезненно добавлять новые поля. Есть более компактный и шустрый аналог — protobuf. Еще можно на boost serialization посмотреть.
Re[2]: Как поддерживать старый формат файла?
От: rsdh  
Дата: 21.05.12 22:20
Оценка:
Здравствуйте, enji, Вы писали:

E>Здравствуйте, rsdh, Вы писали:


E>А сколько данных? Например, если сохранять в xml — можно безболезненно добавлять новые поля. Есть более компактный и шустрый аналог — protobuf. Еще можно на boost serialization посмотреть.


Данных много, гигабайт наберется. protobuf смотрел, пользовал, но тут не тот случай. Структура файла скорее похожа на образ, есть управляющие данные описывающие расположение в файле полезной информации, а есть собственно сама полезная информация (в больших объемах). Структуры в исходном сообщении по идее должны описывать расположение (смещение/размер) полезной информации в файле.
Вопрос на самом деле не про сериализацию данных, а про разные версии кода, обрабатывающего эти данные.

Есть программа с некоторым функционалом умеющая писать какой-то файл.
Есть "новая" версия этой программы с расширенным функционалом. Код этой программы насквозь отличается мелочами от предыдущей версии. Как совместить в это программе "старую" и "новую" версию?
файл -- читатель -- ядро -- писатель -- файл

В исходном сообщении я написал как я разделил версии "файл". Как быть с читателем, писателем и ядром? Мой вариант — аналогично структурам, вынести все классы-обработчики данных, имеющие какое-либо отношение к функционалу порождающему отличия в версиях, в пространства имен и использовать так

Tool::Reader::CurrentVersion::Add(Tool::FileFormat::CurrentVersion::SomeData1)
Tool::Reader::Version0::Add(Tool::FileFormat::Version0::SomeData0)


Про boost serialization.
Re[3]: Как поддерживать старый формат файла?
От: enji  
Дата: 22.05.12 06:11
Оценка:
Здравствуйте, rsdh, Вы писали:

R>В исходном сообщении я написал как я разделил версии "файл". Как быть с читателем, писателем и ядром? Мой вариант — аналогично структурам, вынести все классы-обработчики данных, имеющие какое-либо отношение к функционалу порождающему отличия в версиях, в пространства имен и использовать так


Возможно проще сделать конвертеры, понижающие\повышающие версию файла. Тогда основная программа всегда работает с самой новой версией, а при загрузке \ сохранении при необходимости запускается конвертер. Однако при гигабайте данных возможны тормоза. С другой стороны, конвертер запускается сравнительно редко.

Если в файле просто лежат массивы структур — можно описать содержимое структуры:

struct FieldDescr
{
  unsigned id;
  unsigned size;
  enum Type { Int, Float, Char, ... } type; 
  unsigned arraySize;
};

struct StructDescr
{
  vector<FieldDescr> fields;
};

void convert(StructDescr srcDescr, const void *src, StructDescr trgFile, void *trg)
{
  // анализирует описание полей и выполняет преобразования
}
Re[3]: Как поддерживать старый формат файла?
От: enji  
Дата: 22.05.12 07:04
Оценка:
Здравствуйте, rsdh, Вы писали:

R>Данных много, гигабайт наберется. protobuf смотрел, пользовал, но тут не тот случай.


А кстати почему — не тот случай? Протобуф как раз и был изобретен для хранения большого числа не очень больших объектов... Кроме того, он закроет все проблемы с изменением версии компилятора, 32/64 и т.д.
Re[4]: Как поддерживать старый формат файла?
От: Кодт Россия  
Дата: 22.05.12 08:17
Оценка: +1
Здравствуйте, enji, Вы писали:

E>Если в файле просто лежат массивы структур — можно описать содержимое структуры:


В составе структуры может быть поле с версией этой структуры. Так, например, некоторые WinAPI поступают (TAPI, RichText...).
Иногда версию структуры можно определить по её размеру, т.е. поле cbSize служит для этих целей.
Перекуём баги на фичи!
Re[2]: Как поддерживать старый формат файла?
От: Mr.Delphist  
Дата: 22.05.12 09:20
Оценка:
Здравствуйте, enji, Вы писали:

E>А сколько данных? Например, если сохранять в xml — можно безболезненно добавлять новые поля.


Проблема не в полях — бинарный парсер тоже можно натаскать на пропуск неизвестных частей. Основная опа-опа начинается при изменении смысла сущности. Например, из линейной коллекции надо сделать дерево, из кортежа — коллекцию, из двух сущностей — три с половиной и т.п.
Re[5]: Как поддерживать старый формат файла?
От: rsdh  
Дата: 22.05.12 11:43
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, 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)
    };
};
Re[4]: Как поддерживать старый формат файла?
От: rsdh  
Дата: 22.05.12 11:53
Оценка:
Здравствуйте, enji, Вы писали:

E>Здравствуйте, rsdh, Вы писали:


R>>Данных много, гигабайт наберется. protobuf смотрел, пользовал, но тут не тот случай.


E>А кстати почему — не тот случай? Протобуф как раз и был изобретен для хранения большого числа не очень больших объектов... Кроме того, он закроет все проблемы с изменением версии компилятора, 32/64 и т.д.


Не тот случай, потому проблема не в сериализации — нет смысла использовать protobuf, там и без него все просто. Проблема в организации разных версий кода. В разных версиях программы будет разный код ядра обрабатывающий/формирующий данные файла и protobuf тут не поможет.
Re: Как поддерживать старый формат файла?
От: Vzhyk  
Дата: 22.05.12 12:17
Оценка: +1
22.05.2012 0:34, rsdh написал:

Во-первых, не пытаться предсказать будущее.
Во-вторых, вначале файле положить текстовое поле с номером версии.
Все. Остальное будете делать по мере изменения задачи.
Posted via RSDN NNTP Server 2.1 beta
Re[2]: Как поддерживать старый формат файла?
От: RSDHost  
Дата: 22.05.12 12:29
Оценка:
Здравствуйте, Vzhyk, Вы писали:

V>22.05.2012 0:34, rsdh написал:


V>Во-первых, не пытаться предсказать будущее.

V>Во-вторых, вначале файле положить текстовое поле с номером версии.
V>Все. Остальное будете делать по мере изменения задачи.

Тут и предсказывать нечего. Формат точно изменится — все фичи с наскоку не реализовать, а добавляя их в будущем неизбежно придется менять формат.
Хочется подготовить архитектуру для этих изменений сейчас, когда это еще можно сделать.
Про версию писал выше
Автор: rsdh
Дата: 22.05.12
.
В любом случае, спасибо!
Re: Как поддерживать старый формат файла?
От: Vamp Россия  
Дата: 22.05.12 12:55
Оценка:
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";
}


Разве нет?
Да здравствует мыло душистое и веревка пушистая.
Re[2]: Как поддерживать старый формат файла?
От: RSDHost  
Дата: 22.05.12 13:17
Оценка:
Здравствуйте, 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, изменилось ядро для работы с этим классом. Как результат — требуется в файл записать инфу о том, кто, куда, как, зачем и почему побежал. Завтра программа научится строить какие-нибудь графики, шифровать или архивировать сохраняемые данные и тд...
Т.е. меняется не только читатель и писатель, а все ядро. Нужно сохранить старый код для работы со старыми форматами, добавить новый для нового. Хотел разделить версии в разные пространства имен, здесь
Автор: rsdh
Дата: 22.05.12
писал, но не уверен, что это правильно. Надеялся, что тут подскажут какой нибудь хитрый паттерн, дабы велосипед не городить.
Re[3]: Как поддерживать старый формат файла?
От: Vzhyk  
Дата: 22.05.12 13:39
Оценка: 1 (1)
22.05.2012 15:29, RSDHost написал:

> Тут и предсказывать нечего. Формат точно изменится — все фичи с наскоку

> не реализовать, а добавляя их в будущем неизбежно придется менять формат.
> Хочется подготовить архитектуру для этих изменений сейчас, когда это еще
> можно сделать.
Я написал, исходя из моего опыта. Раньше я тоже пытался предугадывать
что-то, архитектуру продумывать. В итоге, я убедился, что единственный
гарантированно работающий вариант, что написал выше.
Ну и еще в качестве формата xml удобен оказался.
Все остальное лучше вначале не наворачивать вообще.
Posted via RSDN NNTP Server 2.1 beta
Re[4]: Как поддерживать старый формат файла?
От: Vzhyk  
Дата: 22.05.12 13:44
Оценка:
22.05.2012 16:39, Vzhyk написал:

> Ну и еще в качестве формата xml удобен оказался.

> Все остальное лучше вначале не наворачивать вообще.
И еще отказался от структур в коде на С для хранения подобного.
Использую подобие xpath: value = get_value("/root/field1/field2",
def_value).
Соответственно с версиями в таком подходе проблем нет.
Posted via RSDN NNTP Server 2.1 beta
Re[3]: Как поддерживать старый формат файла?
От: enji  
Дата: 22.05.12 22:54
Оценка:
Здравствуйте, RSDHost, Вы писали:

RSD>Да. Все так. Только там зависимость от формата идет чуть глубже, чем писатель/читатель.

RSD>Объясню с другой стороны. Добавляем в софт новую фичу. Теперь программа умеет бегать за пивом, появился класс СBuyBeer, изменилось ядро для работы с этим классом. Как результат — требуется в файл записать инфу о том, кто, куда, как, зачем и почему побежал. Завтра программа научится строить какие-нибудь графики, шифровать или архивировать сохраняемые данные и тд...
RSD>Т.е. меняется не только читатель и писатель, а все ядро. Нужно сохранить старый код для работы со старыми форматами, добавить новый для нового. Хотел разделить версии в разные пространства имен, здесь
Автор: rsdh
Дата: 22.05.12
писал, но не уверен, что это правильно. Надеялся, что тут подскажут какой нибудь хитрый паттерн, дабы велосипед не городить.


Дык а зачем иметь в одной программе весь код? Надо ли вообще иметь возможность сохранять в старом формате или достаточно только нового?

Даже если нужна конвертация в обе стороны, ее можно оформить именно как конвертацию — сконвертировать форматы проще, чем поддерживать работу с обоими. При этом вся программа поддерживает только актуальный формат и написаны понижающие и повышающие конвертеры
Re[4]: Как поддерживать старый формат файла?
От: RSDHost  
Дата: 23.05.12 09:43
Оценка:
Здравствуйте, enji, Вы писали:

E>Здравствуйте, RSDHost, Вы писали:


E>Дык а зачем иметь в одной программе весь код? Надо ли вообще иметь возможность сохранять в старом формате или достаточно только нового?

E>Даже если нужна конвертация в обе стороны, ее можно оформить именно как конвертацию — сконвертировать форматы проще, чем поддерживать работу с обоими. При этом вся программа поддерживает только актуальный формат и написаны понижающие и повышающие конвертеры

Ну т.е. весь саппортный код переедет из ядра в конвертер? Чем это лучше? По факту в конвертере будет 80% кода ядра, потому что конвертация на понижение версии потребует удаления из файла данных неподдерживаемых старой версией, а чтобы их правильно удалить, нужно обработать инфу из файла, а это значит нужно тащить обрабатывающий код в конвертер. Это совсем никуда не годится.

В общем, всем спасибо. Останусь на исходном варианте — каждую версию в свое пространство имен, по версии файла использовать нужный набор классов.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.