eao197 on the Web
Сайт Евгения Охотникова
[ Главная | Проекты | Описания | Об авторе | Лицензия ]

C++ tricks: local_class

При реализации в ObjESSty способа хранения размерности контейнеров в стиле ASN1 BER возникла задача:
- если значение размерности меньше или равно 127, то значение храниться в одном байте;
- если значение больше 127, то первый байт хранит количество байт с двоичным образом размерности. При этом старший, седьмой, бит первого байта установлен в единицу, а биты с шестого по нулевой содержат количество байт, расположенных следом.

Первоначальный набросок реализации выглядел так:

const oess_1::uint_t max_quantity_image_size = 5;
...
void
obinstream_t::write( const oess_1::defs::quantity_t & p )
{
   oess_1::uchar_t image[ max_quantity_image_size ];
   oess_1::uint_t length = 1;
   
   oess_1::uint_t v = p.value();
   if( v <= 127u )
      // Достаточно всего одного байта для сохранения длины.
      image[ max_quantity_image_size - length ] = static_cast< oess_1::uchar_t >( v );
   else
      {
         // Потребуется еще и первый байт, в котором будет выставлен
         // старший бит и будет содержаться количество байт с
         // представлением quantity.
         for(; v; ++length, v >>= 8 )
            {
               image[ max_quantity_image_size - length ] = static_cast< oess_1::uchar_t >( v & 0xffu );
            }
         // Особенность данного алгоритма в том, что на момент выхода
         // из цикла length на единицу больше количества байт,
         // в которых сохранено значение quantity. Т.е. length
         // содержит количество байт, которые должно быть записано в
         // выходной поток.
         image[ max_quantity_image_size - length ] =
            static_cast< oess_1::uchar_t >( ( image.length() - 1 ) | 0x80u );
      }

   write( &image[ max_quantity_image_size - length ], length );
}

Но меня смущало слишком много вычислений индексов при обращении к image и слишком много static_cast-ов. Среди этих продробностей терялась логика самого алгоритма. Для того, чтобы отделить логику от лишних деталей был сделан вспомогательный класс, который отвечает за хранение image и length. А для того, чтобы класс не засорял глобальное пространство имен, он был сделан локальным классом в методе write().

В результате код write() получился более объемным, но зато гораздо более понятным для меня:

void
obinstream_t::write( const oess_1::defs::quantity_t & p )
{
   /*!
    * Вспомогательный класс для инкапсуляции манипуляций с содержимым
    * вектора двоичного образа.
    */
   class image_manipulator_t
      {
         oess_1::uchar_t m_image[ max_quantity_image_size ];
         oess_1::uint_t m_length;
      public :
         image_manipulator_t() : m_length( 1 ) {}

         void
         store( oess_1::uint_t v )
            {
               m_image[ max_quantity_image_size - m_length ] =
                     static_cast< oess_1::uchar_t >( v );
            }
         void
         next()
            {
               ++m_length;
            }
         const oess_1::uint_t
         length() const
            {
               return m_length;
            }
         const oess_1::uchar_t *
         ptr() const
            {
               return &m_image[ max_quantity_image_size - m_length ];
            }
      };

   image_manipulator_t image;

   oess_1::uint_t v = p.value();
   if( v <= 127u )
      // Достаточно всего одного байта для сохранения длины.
      image.store( v );
   else
      {
         // Потребуется еще и первый байт, в котором будет выставлен
         // старший бит и будет содержаться количество байт с
         // представлением quantity.
         for(; v; image.next(), v >>= 8 )
            {
               image.store( v & 0xffu );
            }
         // Особенность данного алгоритма в том, что на момент выхода
         // из цикла image.length() на единицу больше количества байт,
         // в которых сохранено значение quantity. Т.е. image.length()
         // содержит количество байт, которые должно быть записано в
         // выходной поток.
         image.store( ( image.length() - 1 ) | 0x80u );
      }

   write( image.ptr(), image.length() );
}

© 2003-2005 Е.А. Охотников
$LastChangedDate: 2006-06-16 14:40:57 +0400 (РџС‚, 16 РёСЋРЅ 2006) $
e-mail

Hosted by uCoz