Использование метаданных в программах на языке C++
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 04.09.05 19:55
Оценка: 30 (6)
#Имя: FAQ.cpp.metadata
Здравствуй, Влад

E>>Имхо, удалось показать, что метаданные на шаблонах это слишком мощный (я бы даже сказал overkill) механизм. Что-то на нем очень круто как-то делается. Только вот что делается, почему это делается именно так, и стоило ли это делать именно так -- для меня остается загадкой. Вот серьезно, я не понял, почему на примере сериализации структур делается вывод о полезности метаинформации.


А>Сериализация — это классический пример использования метаданных.

А>Загляни в дотнет — там активно они используются (например, XmlSerializer). Существенное отличие в том, что в дотнете используются кодогенерация и компиляция на лету.

А>Задача была показать — что аналогичные возможности доступны и в С++.


Поскольку у меня есть нескоторый опыт
Автор: eao197
Дата: 24.01.05
, то могу сказать, что сериализация -- это классический пример весьма сложной задачи, решать которую можно разными способами. Один способ -- это создание метаданных вручную в коде программы. По этому пути пошли авторы boost::serialization, s11n.net и ты в своей статье. Если поднять обсуждения проблем сериализации и рефлекшена здесь и в форуме "C/C++ Прикладные вопросы", то такая точка зрения многим симпатична.

Еще один способ -- создание метаданных автоматически на основе парсинга C++ кода. Активным ее проповедником здесь является adontz (см., например, темы Проект сериализации [просьба высказаться]
Автор: adontz
Дата: 02.01.05
, Проект сериализации — 2 [просьба высказаться]
Автор: adontz
Дата: 08.01.05
, Проект сериализации — 3 [просьба высказаться]
Автор: adontz
Дата: 25.05.05
Проект Visual Generator
Автор: adontz
Дата: 07.07.05
).

Ну и еще один способ -- это наличие внешнего описания на Data Definition Language (DDL) из которого генерируется код сериализации/десериализации данных. Сторонником этого подхода являюсь я сам (в моем проекте ObjESSty так и происходит). По такому принципу работают ASN1 компиляторы (только там еще и описания C++ классов генерируются). Что-то подобное с IDL применяется в CORBA и Ice.

Лично мне последний способ представляется наиболее удобным, т.к. тогда мы получаем возможность делать более простые и, одновременно, более удобные описания. Например, на моем ObjESSty DDL описание структуры SPoint выглядело бы:
{type SPoint
    {attr x {of oess_1::int_t}}
    {attr y {of oess_1::int_t}}
}

(правда, ObjESSty пока не умеет делать XML-сериализацию, зато есть другие фишки). А из такого описания строится довольно-таки сложный вспомогательный код (пример которого можно посмотреть здесь
Автор: eao197
Дата: 19.04.05
).

Наличие отдельного, декларативного описания схемы сериализуемых данных позволяет получать дополнительные преимущества:
— простота описания (особенно в случае, когда сериализуемые C++ классы полностью генерируются по DDL описанию);
— возможность по одному описанию строить разные схемы сериализации/десериализации. Например, в твоем подходе для XML-сериализации сразу указываются имена XML элементов и атрибутов. И жестко фиксируются в метаданных. А что делать, если SPoint может быть сериализован в разных XML схемах и названия элементов и атрибутов должны быть разными? В DDL можно сделать что-то подобное:
{type SPoint
    {xml
        {scheme
            {name "http://www.superpuper.com/xml/schemas/scheme1" {alias "scheme1"}}
            {element "point"}}
        {scheme    {name "http://www.megapupersuper.com/schames/another" {alias "scheme2"}}
            {element "PT"}}
    }
    
    {attr x {of oess_1::int_t}
        {xml
            {scheme {alias "scheme1"}} {attr "x"}}
            {scheme {alias "scheme2"}} {element "x"}}
        }
    }
    {attr y {of oess_1::int_t}
        {xml
            {scheme {alias "scheme1"}} {attr "y"}}
            {scheme {alias "scheme2"}} {element "y"}}
        }
    }
}

И при генерации вспомогательного кода указывать, код для какой XML схемы нужно генерировать.
Или можно даже разнести описания самой структуры SPoint и способов ее отображения на XML:
{type SPoint
    {attr x {of oess_1::int_t}}
    {attr y {of oess_1::int_t}}
}

{xml-reflection {scheme "http://www.superpuper.com/xml/schemas/scheme1"}
    {type-reflection SPoint
        {element "point"}
        {attr x {as {attribute "x"}}}
        {attr y {as {attribute "y"}}}
    }
}

{xml-reflection {scheme "http://www.megapupersuper.com/schames/another"}
    {type-reflection SPoint
        {element "PT"}
        {attr x {as {element "x"}}}
        {attr y {as {element "y"}}}
    }
}

При этом дополнительные описания {xml-reflection} можно дополнять уже после того, как основной вспомогательный код для SPoint будет сгенерирован, скомпилирован и влинкован. Более того, можно даже сделать так, чтобы описания {xml-reflection} подгружались динамически и сериализация в новую XML схему выполнялась путем интерпретации описания {xml-reflection};
— возможность по DDL описанию генерировать код для поддержки этого типа в другом языке. Например, когда сериализованные C++ кодом данные должны быть прочитаны из Python или Ruby;
— возможность делать автоматические средства для трансформирования сериализованных данных при эволюции схемы данных.

Достижения аналогичных целей путем описания метаданных непосредственно в C++ коде, имхо, гораздо более трудоемко. Причем эта трудоемкость будет заключатся в том, что четырех-пятиэтажные шаблонные конструкции в C++ коде придется оборачивать сложными кружевами макросов. В одном своем проекте я подобную глупость допустил. Теперь нужно приложить массу усилий чтобы избавится от ее последствий.

Так что пример с применением предложенной тобой схемы сериализации лично для меня выглядит не очень удачным. Но это из-за того, что я сам сериализацией занимался и у меня другой взгляд на то, как это должно быть.

А>Есть наметки как решить существующие проблемы (статья + пример).

А>Что бы сделать полноценную библиотеку нужно время. Если бы кто-нибудь за это взялся — я бы с удовольствием попользовался результатами

Ну дык быблиотек для С++ сериализации не так уж и мало. Неужели ни одна не подходит?

E>>Поэтому, если задачей было показать необходимость подобного механизма в стандартной библиотеке C++, то, имхо, эта задача достигнута не была. Чесно говоря я вообще не понял, что за компонент должен быть в стандарной библиотеке и что этот компонент должен делать.

А>Стандартный способ описания и использования метаданных.

Имхо, нет такого понятия, как стандартный способ описания и использования метаданных. Поскольку метаданные привязаны к конкретной задаче. Для сериализации в XML -- нужен один тип метаданных, для сериализации в ASN1 -- совсем другой, для сериализации в YAML -- третий. А для создания более продвинутого аналога boost::signal (с возможностью выбора разных нитей для разных обработчиков сигналов) нужны будут совсем другие метаданные.

Один подход для их использования (который, имхо, сейчас очень популярен) -- это эксплуатация на полную катушку возможностей метапрограммирования в C++ с шаблонами. Лично мне этот подход не нравится. Потому, что получается очень трудновоспринимаемый код. Очень уж write-only код выходит. В boost когда-то предлагали включить библиотеку FSM (для описания конечных автоматов) -- это было нечто

А вот второй подход, основанный на использовании DSL (Domain Specific Language) мне гораздо более симпатичным. Создается простой язык для конкретной предметной области. Создается его транслятор и генератор вспомогательного кода. И создается необходимый набор библиотек для поддержки сгенерированного кода. Каждый компонентик оказывается относительно простым как в написании, так и в использовании. Но, что более важно, -- в сопровождении. Для сопровождения транслятора, созданного на основе, например, bison/flex, совсем не требуется гуру в области C++ шаблонов.

А еще интереснее то, что для DSL не обязательно создавать отдельные специализированные языки. Вполне возможно использовать некоторые существующие языки, которые способствуют метапрограммированию. Например, Lisp (см. Metaprogramming et al
Автор:
Дата: 09.07.05
, Re: Metaprogramming et al: Ruby?
Автор: eao197
Дата: 10.08.05
, Re[2]: Metaprogramming et al: Ruby?
Автор: eao197
Дата: 19.08.05
, Вот такой вот препроцессор.
Автор: c-smile
Дата: 20.08.05
, Re: Вот такой вот препроцессор.
Автор: eao197
Дата: 20.08.05
). Например, приведенные мной выше DDL-фрагмены можно было бы представить на Ruby чем-то вроде:
type :SPoint {
    attr :x, :oess_1::int_t
    attr :y, :oess_1::int_t
}

xml_reflection :scheme => "http://www.superpuper.com/xml/schemas/scheme1" { |s|
    s.type_reflection :SPoint, :element => "point" { |t|
        t.attr :x, :attribute => "x"
        t.attr :y, :attribute => "y"
    }
}

xml_reflection :scheme => "http://www.megapupersuper.com/schames/another" { |s|
    s.type_reflection :SPoint, :element => "PT" { |t|
        t.attr :x, :element => "x"
        t.attr :y, :element => "y"
    }
}

При этом нам не нужно писать ни лексический анализатор, ни синтаксический анализатор. Можно сразу создавать прикладные классы для хранения описания в Ruby коде и генерацию по этим описаниям C++ кода.

А>Если есть вопросы — я на них с удовольствием отвечу.


Да нет, вопросов как раз нет. Есть просто впечатление о статье, которое я и высказал. Надеюсь, оно окажется полезным.
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.