void send_sms( const destination_t & dest, const destination_t & src, const sms_encoding_t & encoding, const std::string & sms_body ) throw( std::exception );
Изменение прототипа функции send_sms для удовлетворения возрастающих потребностей является тупиковым вариантом:
Возможность расширения списка параметров функции send_sms можно было бы осуществить с помощью т.к. карты свойств (traits map):
class trait_t { public : virtual ~trait_t(); virtual int trait_id() const = 0; }; typedef std::map< int, trait_t * > trait_map_t; void send_sms( const destination_t & dest, const destination_t & src, const sms_encoding_t & encoding, const std::string & sms_body, const trait_map_t * traits = 0 ) throw( std::exception );
Т.е. когда необходимо, скажем, поддержать в send_sms возможность таймаута ожидания результата доставки SMS и обеспечить приоритетную обработку, то создаются специальные классы свойств:
class timeout_trait_t : public trait_t { public : timeout_trait_t( unsigned int millisec ); virtual ~timeout_trait_t(); virtual int trait_id() const; unsigned int timeout() cont; }; class priority_trait_t : public trait_t { public : priority_trait_t( int priority ); virtual ~priority_trait_t(); virtual int trait_id() const; int priority() cont; };
trait_map_t traits; timeout_trait_t timeout( 5000 ); traits[ timeout.trait_id() ] = &timeout; priority_trait_t priority( -1 ); traits[ priority.trait_id() ] = &priority; send_sms( dest, src, encoding, sms, &traits );
class send_sms_request_t : public oess_1::stdsn::serializable_t { OESS_SERIALIZER( send_sms_request_t ) private : destination_t m_dest; destination_t m_src; sms_encoding_t m_encoding; std::string m_sms_body; ... };
{type send_sms_request_t {attr m_dest {of destination_t}} {attr m_src {of destination_t}} {attr m_encoding {of sms_encoding_t}} {attr m_sms_body {of std::string}} }
Когда придет время расширять функциональность запроса send_sms_request_t придется учитывать, что клиент и сервер могут поддерживать разные версии send_sms_request_t. Поэтому решение с передачей в запросе send_sms_request_t карты свойств в чистом виде становиться невозможным. Действительно, что произойдет, если описать send_sms_request_t следующим образом:
{type trait_t {abstract} } {type send_sms_request_t {attr m_dest {of destination_t}} {attr m_src {of destination_t}} {attr m_encoding {of sms_encoding_t}} {attr m_sms_body {of std::string}} {attr m_traits {stl-map {key int}} {of {ptr} trait_t} } }
Можно было бы сериализовать объекты по указателям так, чтобы сохранять размер сериализованного объекта. А затем использовать это значение для пропуска неизвестных объектов. Но тогда будут просто проигнорированы критически важные свойства. Например, если рассылка SMS по нескольким адресам осуществлятся при помощи свойства group_send_trait_t, то сервер, который не знает про это свойство, должен отказаться выполнять запрос. Например, пусть базовый класс trait_t будет не абстрактным, а содержащим атрибут m_must_understand, который показывает, можно ли игнорировать данное свойство:
{type trait_t {attr m_must_understand {of oess_1::int_t}} }
В таких случаях сервер должен быть способен извлечь из сериализованного неизвестного ему свойства ту часть, которая отностится к типу trait_t, а все остальное проигнорировать.
Именно эта возможность реализована в механизме subclassing_by_extension. С его помощью описанная ситуация решается следующим образом:
{type trait_t {extensible} {subclassing_by_extension} {attr m_must_understand {of oess_1::uint_t}} } {type send_sms_request_t {attr m_dest {of destination_t}} {attr m_src {of destination_t}} {attr m_encoding {of sms_encoding_t}} {attr m_sms_body {of std::string}} {attr m_traits {stl-map {key int}} {of {extension_of} trait_t} } } {type timeout_trait_t {extensible} {subclassing_by_extension {extension_of trait_t}} {attr m_timeout {of oess_1::uint_t}} } {type priority_trait_t {extensible} {subclassing_by_extension {extension_of trait_t}} {attr m_priority {of oess_1::int_t}} }
Именно из-за того, что атрибуты производных типов сериализуются в специальное расширение базового типа, данный механизм получил название subclassing_by_extension.
{subclassing_by_extension}
. У типа, являющегося вершиной иерархии расширяемых таким образом типов, указывается тег {subclassing_by_extension} без дополнительных подтегов: {type trait_t {extensible} {subclassing_by_extension} {attr m_must_understand {of oess_1::uint_t}} }
{extension_of}
с именем базового типа: {type timeout_trait_t {extensible} {subclassing_by_extension {extension_of trait_t}} {attr m_timeout {of oess_1::uint_t}} } {type priority_trait_t {extensible} {subclassing_by_extension {extension_of trait_t}} {attr m_priority {of oess_1::int_t}} }
Для указания того, что атрибут является указателем на расширяемый наследованием тип, должен использоваться тег {extension_of}
при указании типа атрибута:
{attr m_traits {stl-map {key int}} {of {extension_of} trait_t} }
std::map< int, trait_t * > m_traits;
Механизм subclassing_by_extension использует специальный формат сериализации, который отличается от формата сериализации обычного наследования. Поэтому, наследование в рамках subclassing_by_extension указывается с помощью комбинации {subclassing_by_extension {extension_of}}, а не с помощью {super}. В сочетании с возможностью использования только одиночного наследования это так же означает, что нельзя одновременно указывать {subclassing_by_extension {extension_of}} и {super} в описании типа. Между тем, вершина иерархии расширяемых наследованием типов может быть обычным образом производна от любых других типов:
{type A ... } {type B ... } {type C || Здесь можно использовать обычное наследование. {super A} {super B} {subclassing_by_extension} ... } {type D || Здесь уже нельзя использовать обычное наследование || и теги {super}. {subclassing_by_extension {extension_of C}} ... }
Вершина иерархии расширяемых наследованием типов не может быть абстрактным типом, т.к. объекты этого типа создаются на десериализующей стороне, если реальный сериализованный тип этой стороне не известен.
Если какой-то тип обычным образом унаследован от раширяемого наследованием типа, то объекты этого типа не должны использоваться в атрибутах {extension_of}. Т.е. следующая конструкция недопустима:
|| || DDL || {type A {subclassing_by_extension} ... } {type B {attr a {of {extension_of} A }} } {type C {super A} ...}
// // C++ // B b; b.a = new C(); // ОШИБКА!
{type trait_t {subclassing_by_extension} ... } {type cipher_t {abstract} {subclassing_by_extension {extension_of trait_t}} ... } {type symmetric_cipher_t {subclassing_by_extension {extension_of cipher_t}} ... } {type tdes_cipher_t {subclassing_by_extension {extension_of symmetric_cipher_t}} ... } {type aes_cipher_t {subclassing_by_extension {extension_of symmetric_cipher_t}} ... } {type request_t ... {attr m_traits {stl-map {key oess_1::uint_t}} {of {extension_of} trait_t}} }
{type blowfish_cipher_t {subclassing_by_extension {extension_of symmetric_cipher_t}} ... }
Для того, чтобы можно было создавать объекты ближайшего базового типа при десериализации, в сериализованном представлении ObjESSty сохраняет имена всех типов, которые составляют цепочку наследования. Т.е. для приведенного примера, в сериализованном представлении объекта типа blowfish_cipher_t будет сохранен список имен: {blowfish_cipher_t, symmetric_cipher_t, trait_t}.
Имена абстрактных типов, которые входят в иерархию наследования сериализуемого типа, в описании цепочки наследования не сохраняются, т.к. объекты абстрактных типов не могут быть созданы и десериализованны.
{type key_t {extensible} {attr m_index {of oess_1::uint_t}} } {type value_t {extensible} {subclassing_by_extension} {attr m_value {of std::string}} } {type data_t {attr m_data {of {extension_of} value_t} {stl-map {key key_t}}} } {type timeout_value_t {extensible} {subclassing_by_extension {extension_of value_t}} {attr m_millisec {of oess_1::uint_t}} } {type compression_value_t {extensible} {subclassing_by_extension {extension_of value_t}} {attr m_preferred {of std::string}} }
{type key_t {extensible} {attr m_index {of oess_1::uint_t}} } {type value_t {extensible} {subclassing_by_extension} {attr m_value {of std::string}} {extension {attr m_version {of std::string} {default {c++ \"1.0\"}}} } } {type data_t {attr m_data {of {extension_of} value_t} {stl-map {key key_t}}} } {type timeout_value_t {extensible} {subclassing_by_extension {extension_of value_t}} {attr m_millisec {of oess_1::uint_t}} {extension {attr m_period {of oess_1::uint_t} {default {c++ 0 }}} } } {type compression_value_t {extensible} {subclassing_by_extension {extension_of value_t}} {attr m_preferred {of std::string}} {extension {attr m_level {of oess_1::short_t} {default {c++ 9}}} } } {type signature_value_t {extensible} {subclassing_by_extension {extension_of value_t}} {attr m_algorithm {of std::string}} }
{type A {subclassing_by_extension} {attr a {of oess_1::uint_t}} {attr b {of oess_1::uint_t}} }
struct A_bin_image { oess_1::uint_t a; oess_1::uint_t b; oess_1::uint_t derived_image_size; oess_1::char_t derived_image[ derived_image_size ]; };
Производный при помощи расширения тип B:
{type B {subclassing_by_extension {extension_of A}} {attr c {of oess_1::uint_t} }
struct B_bin_image { oess_1::uint_t c; oess_1::uint_t derived_image_size; oess_1::char_t derived_image[ derived_image_size ]; }
Соответственно, в B_bin_image::derived_image могут находиться аналогичные структуры для производных от B классов.
Если же типы A и B являются так же и "просто расширяемыми":
{type A {extensible} {subclassing_by_extension} {attr a {of oess_1::uint_t}} {attr b {of oess_1::uint_t}} } {type B {extensible} {subclassing_by_extension {extension_of A}} {attr c {of oess_1::uint_t} }
struct A_bin_image { oess_1::uint_t a; oess_1::uint_t b; oess_1::uint_t extension_size; oess_1::char_t extension[ extension_size ]; oess_1::uint_t derived_image_size; oess_1::char_t derived_image[ derived_image_size ]; }; struct B_bin_image { oess_1::uint_t c; oess_1::uint_t extension_size; oess_1::char_t extension[ extension_size ]; oess_1::uint_t derived_image_size; oess_1::char_t derived_image[ derived_image_size ]; }