Разбираясь с примерами одной 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 >
|
Итак, вот полный код эксперимента: