суббота, 19 марта 2016 г.

[prog.c++] Продолжаем упарываться шаблонами при парсинге бинарных PDU

Продолжение вчерашней заметки.

Во вчерашнем решении очень не понравилось то, что смещение поля в PDU приходилось дублировать при вызовах encode и decode:

class header16_t
   :  protected data_with_encoder_and_decoder_t< 16 >
{
public :
   auto f1() const { return decode< std::uint16_t >( 0 ); }
   auto f2() const { return decode< std::uint16_t >( 2 ); }
   auto f3() const { return decode< std::uint16_t >( 4 ); }
   auto f4() const { return decode< std::uint16_t >( 8 ); }

   void set_f1( std::uint16_t v ) { encode( 0, v ); }
   void set_f2( std::uint16_t v ) { encode( 2, v ); }
   void set_f3( std::uint16_t v ) { encode( 4, v ); }
   void set_f4( std::uint16_t v ) { encode( 8, v ); }
};

Легко ошибиться. Собственно, я и ошибся: при вызове decode задал правильные смещения, а вот при вызове encode записал неправильные значения. Причем код с этой ошибкой ушел в заметку в блоге и только спустя какое-то время эта ошибка была замечена и исправлена.

Поэтому возник вопрос: а можно ли как-то однократно увязать смещение поле с описанием поля? Чтобы затем смещение автоматически извлекалось из описания поля когда это нужно.

Можно.

Для этого делаем вот такой шаблон:

templatetypename SCALAR, std::size_t INDEX >
struct field_type
{
   using type = SCALAR;

   templatetypename DECODER >
   static type decode( DECODER & d )
   {
      return d.template decode< type >( INDEX );
   }

   templatetypename ENCODER >
   static void encode( ENCODER & e, type value )
   {
      e.encode( INDEX, value );
   }
};

После чего вносятся два небольших изменения в созданную ранее инфраструктуру. Во-первых, класс data_with_encoder_decoder_t использует публичное наследование от encoder_t и decoder_t:

template< std::size_t N >
class data_with_encoder_and_decoder_t
   :  public data_holder_t< N >
   ,  public encoder_t< data_with_encoder_and_decoder_t< N > >
   ,  public decoder_t< data_with_encoder_and_decoder_t< N > >
{};

Нужно это для того, чтобы методы encode и decode были публичными.

Во-вторых, при описании PDU не нужно наследоваться от data_with_encoder_decoder_t. Достаточно просто объявлять атрибут этого типа:

class header16_t
{
   data_with_encoder_and_decoder_t< 16 > m_image;

Ну и с такими изменениями реализация PDU приобретает вид:

class header16_t
{
   using f1_type = field_type< std::uint16_t0 >;
   using f2_type = field_type< std::uint16_t2 >;
   using f3_type = field_type< std::uint16_t4 >;
   using f4_type = field_type< std::uint16_t8 >;

   data_with_encoder_and_decoder_t< 16 > m_image;

public :
   auto f1() const { return f1_type::decode( m_image ); }
   auto f2() const { return f2_type::decode( m_image ); }
   auto f3() const { return f3_type::decode( m_image ); }
   auto f4() const { return f4_type::decode( m_image ); }

   void set_f1( f1_type::type v ) { f1_type::encode( m_image, v ); }
   void set_f2( f2_type::type v ) { f2_type::encode( m_image, v ); }
   void set_f3( f3_type::type v ) { f3_type::encode( m_image, v ); }
   void set_f4( f4_type::type v ) { f4_type::encode( m_image, v ); }
};

class header8_t
{
   using f1_type = field_type< std::uint32_t0 >;
   using f2_type = field_type< std::uint32_t4 >;

   data_with_encoder_and_decoder_t< 8 > m_image;

public :
   auto f1() const { return f1_type::decode( m_image ); }
   auto f2() const { return f2_type::decode( m_image ); }

   void set_f1( f1_type::type v ) { f1_type::encode( m_image, v ); }
   void set_f2( f2_type::type v ) { f2_type::encode( m_image, v ); }
};

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

Конечно, пространство для ошибок все равно остается. Скажем, можно написать:

   auto f3() const { return f4_type::decode( m_image ); }

и такая ошибка будет выявлена только на unit-тестах. Однако, в реальной жизни, скорее всего, вместо f1_type, f2_type и т.д., будет что-то вроде peer_as_number_type, local_as_number_type, interface_index_type и т.д. Поэтому, хоть вероятность глупых опечаток все-таки сохраняется, но она не такая уж и большая.

Ну вот пока и все идеи. Может со временем еще что-нибудь в голову придет.

В online с данным примером можно поиграться здесь: http://goo.gl/xnn9Tt.

Отправить комментарий