Разбираясь с примерами одной C++ной библиотеки обнаружил наличие в разных примерах с парсингом бинарных PDU дублирование в классах для PDU дублирование одних и те же методов encode и decode приблизительно вот такого вида:
void encode( std::size_t index, std::uint16_t v ) { auto b = &(rep()[index]); b[0] = static_cast< std::uint8_t >(v >> 8); b[1] = static_cast< std::uint8_t >(v & 0xffu); } void decode( std::size_t index, std::uint16_t & v ) const { auto b = &(rep()[index]); v = (static_cast< std::uint16_t >(b[0]) << 8) + b[1]; } |
Поскольку не люблю копипасту, то задумался, как можно сделать так, чтобы классы для представления PDU с разного размера payload-ами, имели бы общую реализацию методов encode/decode.
Под катом небольшой пример с результатами эксперимента. Код без комментариев, но, надеюсь, он будет понятен. Если же нет, то отвечу на вопросы в комментариях. Однако, сразу поясню пару вещей, которые могут быть не очевидны.
Сделано два класса encoder_t и decoder_t. Это для того, чтобы можно было делать PDU, которые можно только распаковывать, но не нужно упаковывать. Или, наоборот: PDU, которые нужно только упаковывать, но не распаковывать. Ну и, кроме того, так задачка была сложнее :)
Вот такую конструкцию пришлось сделать для того, чтобы сохранить методы data(), encode() и decode() скрытыми (protected).
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 > > {}; |
Без этого пришлось бы кроме наследования от encoder_t/decoder_t еще и прописывать эти классы в качестве френдов. Что-то вроде:
class header16_t : protected data_holder_t< 16 > , protected encoder< header16_t > , protected decoder< header16_t > { friend class encoder< header16_t >; friend class decoder< header16_t >; |
Вместо намного более короткого варианта:
class header16_t : protected data_with_encoder_and_decoder_t< 16 > |
Итак, вот полный код эксперимента:
#include <cstddef> #include <cstdint> #include <array> #include <iostream> template< class D > class encoder_t { inline std::uint8_t * rep() { return static_cast< D * >(this)->data(); } protected : void encode( std::size_t index, std::uint8_t v ) { (rep()[index]) = v; } void encode( std::size_t index, std::uint16_t v ) { auto b = &(rep()[index]); b[0] = static_cast< std::uint8_t >(v >> 8); b[1] = static_cast< std::uint8_t >(v & 0xffu); } void encode( std::size_t index, std::uint32_t v ) { auto b = &(rep()[index]); b[0] = static_cast< std::uint8_t >(v >> 24); b[1] = static_cast< std::uint8_t >((v & 0xff0000ul) >> 16); b[2] = static_cast< std::uint8_t >((v & 0xff00ul) >> 8); b[3] = static_cast< std::uint8_t >(v & 0xfful); } }; template< class D > class decoder_t { inline const std::uint8_t * rep() const { return static_cast< const D * >(this)->data(); } protected : void decode( std::size_t index, std::uint8_t & v ) const { v = rep()[index]; } void decode( std::size_t index, std::uint16_t & v ) const { auto b = &(rep()[index]); v = (static_cast< std::uint16_t >(b[0]) << 8) + b[1]; } void decode( std::size_t index, std::uint32_t & v ) const { auto b = &(rep()[index]); v = (static_cast< std::uint32_t >(b[0]) << 24) + (static_cast< std::uint32_t >(b[1]) << 16) + (static_cast< std::uint32_t >(b[2]) << 8) + b[3]; } template< typename P > P decode( std::size_t index ) const { P v; decode( index, v ); return v; } }; template< std::size_t N > class data_holder_t { std::array< std::uint8_t, N > m_data; public : const std::uint8_t * data() const { return m_data.data(); } std::uint8_t * data() { return m_data.data(); } }; 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 > > {}; 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 ); } }; class header8_t : protected data_with_encoder_and_decoder_t< 8 > { public : auto f1() const { return decode< std::uint32_t >( 0 ); } auto f2() const { return decode< std::uint32_t >( 4 ); } void set_f1( std::uint32_t v ) { encode( 0, v ); } void set_f2( std::uint32_t v ) { encode( 4, v ); } }; int main() { header16_t h16; h16.set_f1( 0x2189 ); h16.set_f2( 0x1024 ); std::cout << std::hex << h16.f1() << ", " << h16.f2() << std::endl; if( !( 0x2189 == h16.f1() && 0x1024 == h16.f2() ) ) std::abort(); header8_t h8; h8.set_f1( 0x21345678 ); h8.set_f2( 0x43210123 ); std::cout << std::hex << h8.f1() << ", " << h8.f2() << std::endl; if( !( 0x21345678 == h8.f1() && 0x43210123 == h8.f2() ) ) std::abort(); } |
Комментариев нет:
Отправить комментарий