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 ]; }
1.4.7