Здравствуйте, Shady, Вы писали:
S>Здравствуйте, c-smile, Вы писали:
CS>>Если серализуемые данные это нечто типа пассивного DOM (т.е. описываемые в виде struct без методов) то вариант с SERIALIZABLE наверное имеет смысл. CS>>Но если же данные — хотя бы немного активные то тут уже требуется явный доступ к последовательности загрузки/выгрузки. Управление состоянием объекта и пр. S>Если это расматривать с позиции мапирования базы данных, то подход с автоматизацией гораздо лучше. Object Database хотя бы...
Object Database это отдельная ортогональная сериализации песня хотя и звучит похоже.
В Object Database ты описываешь объекты и *связи* между ними.
CS>>Также наверное важно наличие lookahead методов при восстановлении. Типа если есть child nodes создавать под них CS>>контейнер, нет — и не надо. И т.д. S>А что мешает это всё автоматизировать? Может не средствами языка, но парсингом?
Реально запись и чтение из произвольных tagged форматов это настолько разные вещи что в никакой
SERIALIZE их не втиснешь.
Смотри, напрмер в XML, есть два разумных способа: чтение в унифицированный DOM (с его автоматическим построением) и SAX alike parsing в твой собственный DOM (собственно имплементация системы event handlers).
Построить же нечто промежуточное и полууниверсальное можно только в том случае если имеется хоть какая-то metainformation для твоих структур. Собственно SERIALIZE превратится в METAINFO в конце концов.
И вопрос сотоит лишь насколько эта METAINFO будет развита. Например будет ли она допускать наличие например некоего DTD/DDL/schema и т.д.
Либо создать библиотеку типа nano::dom/nano::xml. И пользовать какие-нибудь nano::node в качестве внутреннего представления твоих данных.
Либо написать struct mydata data; fread(&data, sizeof(data)) и гори оно синим пламенем.
Warning C2345: "serialize::silver_bullet — class not found."
Здравствуйте, McSeem2, Вы писали:
MS>Ага, теперь понятно, почему ты хотел сериализовать по умолчанию все поля, за исключением "unserializable".
Типа да
MS>Я бы к этому не сильно стремился — слишком уж много там граблей. А если при этом много legacy-кода с полиморфизмом, да еще и множественным наследованием — начинаются такие дебри и грабли...
Здравствуйте, adontz, Вы писали:
A>OK, ясно. Но понимаешь ли в чём дело. Если данных столько, что их запихали в Oracle, то оверхед в 20-30% по объёму ИМХО не трагедия.
Дык! Разница-то на самом деле — в разы. Некомпактный и компактный медоды с зипованием дают разницу вдвое. Кроме того, при таких объемах, 20-30% по объему, это практически 20-30% и по быстродействию. Бывает, что надо прочитать аж половину 10-гигового файла в режиме random access и выбрать нужные записи. Никакие кэши здесь не помогут. Ну, возможно, что это все моя специфика — гигантские объемы, выжать максимум возможного и все такое... Я это люблю
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Здравствуйте, McSeem2, Вы писали:
MS>К тому же, при грамотном проектировании, объектов, подлежащих сериализации получается не так уж и много, либо они все однотипны.
Т.е. получаем один единственный класс xml:node и пишем для него
процедуры сериализации на все случи жизни.
То же и с DB — в конце концов скатывается к одному едиственному типу — record/field handle. Высе.
Грустно, но похоже что вот собственно и все опции.
Давайте я попробую сформулирвать мое понимание проблемы более формально что-ли:
"SERIALIZE" есть
1) Система функций/модулей для более менее "прозрачной" конверсии C/C++ memory data <-> pesisted data (various forms)
2) Набор сериализуемых типов/значений есть замкнутая система на любой заданный момент времени.
3) Система должна предусматривать версионность. Это так?
4) Преполагается создание неких automation tools для облегчения процесса.
5) Сериализуемые объекты должны быть доступны/представлены родными коснтрукциями C/C++ — struct и class
Как мне кажется путь с заданием SERIALIZEABLE(myclass) ведет в никуда в виду специфики C/C++ и нескольких других факторов.
Надо идти другим путем. Назовем его DDL (data definition language).
Который транслируется один в один в соотв. .h файл готовый к использованию и включению в проект.
Т.е. идея в общем-то проста.
Создание простого С++ подобного языка компилятора позволяющего генерировать .h (.cpp) файлы и некий бинарный ресурс назовем его schema definition.
Имея на руках этот вот schema definition мы сможем уже дальше имплементировать все что нам надо:
XML туда сюда, двоичные данные, имплементация БД и пр.
Т.е. задача сводится к разаработке языка и набору функций read/write у которых первый параметр это schema_difinition* psd
Здравствуйте, c-smile, Вы писали:
CS>Т.е. идея в общем-то проста. CS>Создание простого С++ подобного языка компилятора позволяющего генерировать .h (.cpp) файлы и некий бинарный ресурс назовем его schema definition.
Этот язык называется IDL (не тот, который в COM, а тот, который в CORBA). Берем его, обрезаем собственно интерфейсную часть — то есть, самый основняк и фсе!
CS>Имея на руках этот вот schema definition мы сможем уже дальше имплементировать все что нам надо: CS>XML туда сюда, двоичные данные, имплементация БД и пр.
CS>Т.е. задача сводится к разаработке языка и набору функций read/write у которых первый параметр это schema_difinition* psd
И компиляторы разные есть, для разных языков, в том числе и для C++. Остается только к бинарному CORBовскому маршалеру приделать XML.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Здравствуйте, c-smile, Вы писали:
CS>Построить же нечто промежуточное и полууниверсальное можно только в том случае если имеется хоть какая-то metainformation для твоих структур.
Какия именно? Список полей для класса (пары имя-тип) и так уже есть
CS>И вопрос сотоит лишь насколько эта METAINFO будет развита. Например будет ли она допускать наличие например некоего DTD/DDL/schema и т.д. CS>Либо создать библиотеку типа nano::dom/nano::xml. И пользовать какие-нибудь nano::node в качестве внутреннего представления твоих данных.
У вас какая-то странная методика. Я всё рассматриваю как последовательный поток (без операции seek) сильно напоминающий RIFF.
При записи используються методы: value_set, attribute_set, scope_begin, scope_end
При чтении используются методы: value_get, attribute_get, scope_in, scope_out
Рассмотрим пример — запись вектора строк из трёх элементов "renesans", "baroko", "rokoko"
Вот последовательность вызовов при записи
Не дожидаться конца сериализации перед десериализацией.
Грубо говоря перед использованием архива его нужно ассоциировать с некоторым stream_of_bytes, которым может быть файл или, например, сокет. То есть одна программа сериализует в сокет, а другая не дожидаясь окончания этой операции читает то что уже отослано и десериализует на другом конце.
Что касается версионности, то тут такое дело.
Есть три уровня безопасности:
без проверок
Фактически никакой версионности нет
проверка имён
Хорошо работает когда добавлены новые поля. Если сменили тип поля, то обламывается.
проверка имён и типов
Всегда хорошо работает.
Думаю будет лучше всего, если позволить программисту самому определять какой уровень ему нужен. Если структуры данных не будут меняться,то подходит уровень 1, если будут то 2 или 3.
Идея помечать идентификаторы полей предложенная McSeem2 в коде мне НЕ нравиться. Всё задумывается как система требующая минимального участия программиста. То есть он вообще не должен знать как это работает. Есть класс, в котором данные, есть архив отвечающий за формат хранения этих данных и поток, отвечающий за место хранения. Всё! Как там внутри шестерни крутятся не важно.
Правда McSeem2 жутко прав (и это плохо ) в том что идентификаторы полей надо генерировать уникальными, основываясь фактически только на строках имени поля и имени типа, потому что (viva la versioning!) всё остальное меняется. Учитывая что средняя длина имени скажем 8 символов, а в 4 байта влезет от силы 5.3 символа (это если выкинуть символы не встречающиеся в идентификаторах), мы имеем баальшие проблемы.
С другой стороны описываемое им увеличение объёма данных в 10 раз, это как-то не черезчур. Если я с каждым int буду передавать 4 байта (хеш имени) + 4 байта (хеш типа) то это 12 байт или в три раза больше, но ни как не в 10!
Да есть проблема объёма, но динамическая типобезопасность без метаинформации невозможна, так что за всё надо платить. А вы думаете dynamic_cast как работает? Я вот глядел как внутри _RTTITypeInfo устроен — не маленькая структурка.
Так что либо создаём файлы которые следующая версия программы не прочтёт, либо увеличиваем размер файлов за счёт совместимости. Другого пути я не вижу
Здравствуйте, c-smile, Вы писали:
CS>"SERIALIZE" есть
CS>1) Система функций/модулей для более менее "прозрачной" конверсии C/C++ memory data <-> pesisted data (various forms)
Да
CS>2) Набор сериализуемых типов/значений есть замкнутая система на любой заданный момент времени.
Да
CS>3) Система должна предусматривать версионность. Это так?
В той или иной степени. Как выяснилось желательна настраиваемость, поскольку версионность отражается на конечном объёме данных.
CS>4) Преполагается создание неких automation tools для облегчения процесса.
Да.
CS>5) Сериализуемые объекты должны быть доступны/представлены родными коснтрукциями C/C++ — struct и class
Да.
CS>Как мне кажется путь с заданием SERIALIZEABLE(myclass) ведет в никуда в виду специфики C/C++ и нескольких других факторов.
Выйдете за пределы Си++ Я тут распинаюсь про метаинформацию, а вы Си++...
CS>Надо идти другим путем. Назовем его DDL (data definition language).
Этот другой путь называется IDL (те кто писал хоть что-то под СОМ знают о чём я). Так вот — ты пробовал писать на IDL вручную? Я вот пробовал и надо заметить (да простят меня собеседники) конкретно затрахался!!!
Путь этот тупиковый (ты уж не обижайся, я не только тебя ругаю, но и весь MS ) по одной простой причине — мне НЕ УДОБНО описывать класс на одном языке, а реализовывать на другом. Тем более мне не удобно иметь два описания класса синхронизируемых вручную.
Всё должно быть в одном месте — чтоб было удобно
Более того. я хочу со временем поддерживать свойства (properties), которые как известно есть ни что иное как пара методов (getter, setter) и тут уже никакой сторонний DDL не спасёт. А если придётся создавать (и парсить, не забываем, да?) язык описания структур не менее мошьный чем сам Си++, то нафига он нужен, когда есть Си++, который уже за нас отпарсили и это можно попользовать?
Вобщем товарищи, исходим из того, что вся метаинформация о классе есть и перестаём тыкаться в возможности Си++.
Здравствуйте, adontz, Вы писали:
A>С другой стороны описываемое им увеличение объёма данных в 10 раз, это как-то не черезчур. Если я с каждым int буду передавать 4 байта (хеш имени) + 4 байта (хеш типа) то это 12 байт или в три раза больше, но ни как не в 10!
На самом деле — чуть меньше, чем в 6 раз. В наших задачах целые числа не являются произвольно сгенерированными, или, скажем, значениями CRC32. В 90% случаев их значения укладываются в диапазон байта (но максимальная емкость, конечно же должна быть 32 бита). Итого получаем 1 байт на ID плюс 1 байт на значение плюс некоторый процент на большие значения. Получается 2.1...2.2 байта на целочисленный атрибут. Плюс сокращение в 2...2.5 раза за счет значений по умолчанию. Итого около байта на целочисленный атрибут или даже меньше. Такие дела...
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Здравствуйте, McSeem2, Вы писали:
MS>На самом деле — чуть меньше, чем в 6 раз. В наших задачах целые числа не являются произвольно сгенерированными, или, скажем, значениями CRC32. В 90% случаев их значения укладываются в диапазон байта (но максимальная емкость, конечно же должна быть 32 бита). Итого получаем 1 байт на ID плюс 1 байт на значение плюс некоторый процент на большие значения. Получается 2.1...2.2 байта на целочисленный атрибут. Плюс сокращение в 2...2.5 раза за счет значений по умолчанию. Итого около байта на целочисленный атрибут или даже меньше. Такие дела...
Да! Хорошо что напомнил! Не нравиться мне идея с значениями по умолчанию! Это в корне убивает версионноть! В разных версиях могут быть разные значения по умолчанию для одного и того же поля.
В версии один у тебя будет NULL, а в версии два -1.
В версии один у тебя будет NULL, а в версии два пустая строка.
Чтоб это корректно обрабатывать нужно будет пересылать все значенип о умолчанию перед посылкой класса, если это делать то получаем словарь, от которого ты же и решил отказаться
Здравствуйте, adontz, Вы писали:
A>Этот другой путь называется IDL (те кто писал хоть что-то под СОМ знают о чём я). Так вот — ты пробовал писать на IDL вручную? Я вот пробовал и надо заметить (да простят меня собеседники) конкретно затрахался!!!
Да вроде бы нормально все было... Простой и совершенно ясный язык, вот: http://e-docs.bea.com/wle/cref/member.htm
А то что MS называет IDL — это не язык а, простите, дрисня какая-то в виде крокозябликов. Я так и не понял, как на нем описать и передать структуру данных...
A>Путь этот тупиковый (ты уж не обижайся, я не только тебя ругаю, но и весь MS ) по одной простой причине — мне НЕ УДОБНО описывать класс на одном языке, а реализовывать на другом. Тем более мне не удобно иметь два описания класса синхронизируемых вручную. A>Всё должно быть в одном месте — чтоб было удобно
Никто не мешает сделать такой препроцессор, который бы обрабатывал "вкрапления" IDL в C++ файлы. Но это тоже не хорошо. Вообще, в глобальном смысле, надо разделять функциональность, хоть это и ох как нелегко бывает. Алгоритическая часть никак не должна зависеть от той, которая занимается сериализацией данных.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Здравствуйте, adontz, Вы писали:
A>Да! Хорошо что напомнил! Не нравиться мне идея с значениями по умолчанию! Это в корне убивает версионноть! В разных версиях могут быть разные значения по умолчанию для одного и того же поля. A>В версии один у тебя будет NULL, а в версии два -1. A>В версии один у тебя будет NULL, а в версии два пустая строка.
Возможно ты и прав. Опять же, моя специфика — в органических молекулах более 80% атомов — углероды (водороды вообще не учитываем). Его и делаем значением по умолчанию и поменяться это значение не может (если изобретут силиконовую органику, то там и алгоритмы и все вообще будет другое). Но это еще ладно. Атом может иметь заряд, который в 99.9% случаев равен нулю. Какое должно быть значение по умолчанию? Может ли оно поменяться? Таким образом, значения по умолчанию имеют смысл для величин, так или иначе отражающих физические сущности природы. И поменяться они ну никак не могут...
A>Чтоб это корректно обрабатывать нужно будет пересылать все значенип о умолчанию перед посылкой класса, если это делать то получаем словарь, от которого ты же и решил отказаться
Я не то чтобы отказался... Я просто сказал, что наличие словарей создает дополнительные проблемы, и что это мне не нравится. Иногда эти проблемы становятся весьма серьезными.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Здравствуйте, McSeem2, Вы писали:
MS>Никто не мешает сделать такой препроцессор, который бы обрабатывал "вкрапления" IDL в C++ файлы. Но это тоже не хорошо. Вообще, в глобальном смысле, надо разделять функциональность, хоть это и ох как нелегко бывает. Алгоритическая часть никак не должна зависеть от той, которая занимается сериализацией данных.
Вот поэтому сериализация помечается в описании (declaration) класса и никаких проблем
Тут ведь и инертность публики важна. Если я скажу, что у меня есть супер-пупер сериалайзер для Си++, но вам нужно ещё один язык (пусть даже на что-то похожий) то меня пошлют на... и будут правы.
После такого резюме (новый язык) я бы это дело даже скачивать не стал.
Более того, ещё раз повторю — нам очень выгодно оставаться в рамках Си++ в связи с некоторыми особенностями моих идеек по реализации.
Здравствуйте, adontz, Вы писали:
A>Этот другой путь называется IDL (те кто писал хоть что-то под СОМ знают о чём я). Так вот — ты пробовал писать на IDL вручную? Я вот пробовал и надо заметить (да простят меня собеседники) конкретно затрахался!!! A>Путь этот тупиковый (ты уж не обижайся, я не только тебя ругаю, но и весь MS ) по одной простой причине — мне НЕ УДОБНО описывать класс на одном языке, а реализовывать на другом. Тем более мне не удобно иметь два описания класса синхронизируемых вручную. A>Всё должно быть в одном месте — чтоб было удобно
Честно говоря с IDL я всегда работал через wizards и ничего не могу сказать ни за ни против.
С dbVista и ея DDL работал много — DDL который мапится один в один практически в C это очень удобно.
Также есть опыт работы с POST там метаинформация задется в теле класса как ты предлагаешь — в принципе удобно но менее.
На самом деле тут еще есть момент — внешняя дефиниция данных как-то гибче что-ли.
Можно сделать reader отдельно от writer например. Или построить какую-то универсальную сервис утилиту
которая например берет DDL v.1, данные и DDL v.2 и продуцирует новую версию. И не надо поддерживать
много версионность в самом формате.
A>Более того. я хочу со временем поддерживать свойства (properties), которые как известно есть ни что иное как пара методов (getter, setter) и тут уже никакой сторонний DDL не спасёт. А если придётся создавать (и парсить, не забываем, да?) язык описания структур не менее мошьный чем сам Си++, то нафига он нужен, когда есть Си++, который уже за нас отпарсили и это можно попользовать?
На самом деле распарсить DDL это примерно распарсить скажем CSS файл. Это примерно 500-800 строк кода.
Взять любой сканнер парсер готовый , хотя бы тот же c-smile там все C конструкции есть.
Или какой-нибцдь smallC. Там нотация C++ не нужна.
getter/setter тоже генерируются по описанию типа
struct
{
int field;
property<int> prop;
}
если надо.
A>Вобщем товарищи, исходим из того, что вся метаинформация о классе есть и перестаём тыкаться в возможности Си++.
Откуда она там есть? Т.е. если ты руками не пропишешь нечто типа
Это описание из POST. Хорошо еще что там каждое поле прописывать не надо...
Но смена формата базы это пляска с бубном каждый раз.
Сначала казалось удобным что ты можешь приделать методы к данным в БД.
Но при ближайшем рассмотрении оказалось что и не надо. И даже наоборот — баловство это.
И вообще dbVista — рулез непревзойденный. Немного доточить ея напильником (записи переменной длины, моно файл, unicode) — мечта.
Здравствуйте, adontz, Вы писали:
A>Более того, ещё раз повторю — нам очень выгодно оставаться в рамках Си++ в связи с некоторыми особенностями моих идеек по реализации.
А чем тогда не устраивает решение "в лоб" типа
struct record {
char first_name[32];
char last_name[64];
record() { first_name[0] = 0; last_name[0] = 0; }
void set(const char* first, const char *last)
{
strncpy(first_name, first, sizeof(first_name)-1);
strncpy(last_name, last, sizeof(last_name)-1);
}
};
int main(int argc, char* argv[])
{
tool::table<record> tbl;
//writing/creating table of "record"s
tbl.open("c:/test.tbl",true);
tbl[0].set("Andrew","Fedoniouk");
tbl[1].set("Allan","Doe");
tbl[2].set("Monica","Lester");
tbl.close();
//reading it
tbl.open("c:/test.tbl");
for(unsigned int i = 0; i < tbl.total(); i++)
printf("%s %s\n",
tbl(i).first_name,
tbl(i).last_name );
tbl.close();
return 0;
}
Здравствуйте, adontz, Вы писали:
MS>>Никто не мешает сделать такой препроцессор, который бы обрабатывал "вкрапления" IDL в C++ файлы. Но это тоже не хорошо. Вообще, в глобальном смысле, надо разделять функциональность, хоть это и ох как нелегко бывает. Алгоритическая часть никак не должна зависеть от той, которая занимается сериализацией данных.
A>Вот поэтому сериализация помечается в описании (declaration) класса и никаких проблем A>Тут ведь и инертность публики важна. Если я скажу, что у меня есть супер-пупер сериалайзер для Си++, но вам нужно ещё один язык (пусть даже на что-то похожий) то меня пошлют на... и будут правы.
Это как посмотреть... Вот пример из моего проекта: аффинные 2D преобразования. Матрицу иногда сериализовать надо? Надо. Но я категорически против того, чтобы помечать этот класс некой крокозяблой и таким образом изначально ставить его в зависимость от какой-то там левой сериализации (хоть своей, хоть чужой — не важно). Сериализация этого класса должна выполняться где-то извне его, например, при помощи другого класса, типа serializable_affine_matrix. Более того, для одного и того же класса trans_affine может быть несколько сериализаторов — один сохраняет данные в double, другой — во float, третий — вообще в int32 в формате 16.16. Для четвертого типа может быть вполне достаточно int16 формата 10.6. Для пятого — может быть надо сериализовать только матрицу 2x2 — поворот и масштабирование, без трансляции. В идеале, я должен уметь пользоваться сериализатором следующим образом:
Более того, класс (как тот же trans_affine) может вообще не иметь аксессоров и модификаторов в явном виде. У него есть методы load_from(const double* m6) и store_to(double* m6). Эту ситуацию тоже надо уметь разрулить через некий промежуточный буфер.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Здравствуйте, adontz, Вы писали:
A>Что скажете? Насколько это на ваш взгляд будет удобно/востребованно?
Мои пять копеек....
Было бы хорошо, если бы библиотека предоставляла возможность выбора формата записи целых чисел в открытом формате. Например записывать целое число в xml в 16-ом виде. Это удобно, когда серилизация используется для сохранения конфигурации.
... << RSDN@Home 1.1.4 beta 3 rev. 241>>
"Бог не терпит голой сингулярности" -- Роджер Пенроуз
Уважаемый adontz, а вы в сторону Abstract Syntax Notation One (ASN1) не смотрели? Там уже решены вопросы языка описания данных и их двоичного/текстового представления. Например, из двоичных представлений есть BER (Basic Encoding Rules) в котором каждому полю предшествует пара Tag+Length. И есть более экономичное представление PER (Packed Encoding Rules), в котором поля могут представляться отдельными битами в общем битовом потоке. Для XML есть XER (XML Encoding Rules). Отдельной фишкой ASN1 является понятие расширяемости и т.н. точек расширения.
Проблема с ASN1 для меня была в том, что активных open-source проектов в этом направлении не осталось. А коммерческие решения дорого стоят.
Еще одна проблема, что ASN1 определяет упаковку полей и язык описания сериализуемых структур. Но не определяет как ASN1 описания должны отображаться в конкретный язык программирования, в частности, в С++. Поэтому у каждого поставщика ASN1 решений собственная иерахрия собственных классов для сериализуемых типов. Что сразу же привязывает прикладной код к платформе конкретного производителя asn1-инструментария.
Может вам имеет смысл сделать собственное отображение ASN1 в C++, используя при этом схемы хранения данных из ASN1?
P.S. Я сам работаю над собственным проектом по сериализации и стабилизации сложных C++ данных. Пока этот проект находится в стадии beta-тестирования. Поэтому я о нем информацию в свободный доступ пока не выкладываю. Но, если вам интересно, готов поделиться опытом через e-mail:
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, adontz, Вы писали:
A>Зря ты так о размере заботишься. У тебя подход исходящий из передачи по сети, но есть два момента: A> A>
Если не нужна версионность, то все эти заморочки с метками типов тоже не нужны и передаваться тогда будут только данные. Более того, версионность можно включать для отдельных классов. A>
Что мешает пропустить поток через обычный ZIP? A>
поток не поток, но не всегда есть возможность, а дополнительная компрессия — это лишняя нагрузка на процессор + лишний геморрой.
Здравствуйте, adontz, Вы писали:
SD>>3. SD>>не надо заморачиваться с синтаксисом объявления (мне кажется проблемы с написанием operator<< или идентификаторов полей некритичны) наоборот "чрезмерная автоматизация" процесса через excessive template intantiations с замедлением компиляции в 10 раз (в чем признаются авторы s11n) это плохо.
A>Не волнуйся я думаю уменьшение скорости сборки будет (не компиляции, а сборки) не более чем на 5-10%. Скорость компиляции практически не измениться.
да черт с ней со скоростью компиляции, если человек будет компилировать неправильно, у него при любом раскладе компиляция тормозить будет.
главное чтобы в рантайме все хорошо было.