Упакованные структуры и не упакованные без повторения кода - PullRequest
1 голос
/ 24 июня 2019

У меня есть этот код:

// size probably 4 bytes
struct NotPacked
  {
  uint16_t first;
  uint8_t second;
  };

// size is 3 bytes
struct Packed
  {
  uint16_t first;
  uint8_t second;
  }__attribute__((packed));

Я бы хотел использовать ту же структуру, иногда упакованную, иногда нет. Вы знаете, как написать этот код, избегая повторения?

[Изменить] Вопрос должен был звучать так: «.. написать этот код, избегая как можно большего дублирования кода»

[Edit2] Я попытался провести эксперимент с использованием оптимизации пустого класса, но безуспешно

[Edit3] Добавлен базовый пример:

  Packed packet;
  receivePacketFromNetwork(&packet); // Fill the structure with data coming from the network
  NotPacked notPacked = packedToUnpacked(packet); // convert packed structure to unpacked
  processUnpacked(notPacked); // Do a lot of computations

Ответы [ 3 ]

2 голосов
/ 24 июня 2019

Единственный способ, которым я могу придумать, это использовать некрасивые макросы:

#define DEFINE_BOTH_STRUCTS(PACKED_NAME, REGULAR_NAME, ...) \
    struct PACKED_NAME { __VA_ARGS__ } __attribute__((packed)); \
    struct REGULAR_NAME { __VA_ARGS__ }

И затем использование

DEFINE_BOTH_STRUCTS(Packed, NotPacked, 
    char a;
    int b;
    char c[3];
);

Это определит оба варианта из одного кода.

Существует также менее желательный параметр:

#define MY_STRUCT  { \ 
    char a; \
    int b; \
    char c[3]; \
}
struct Packed MY_STRUCT __attribute__((packed));
struct NotPacked MY_STRUCT;
#undef MY_STRUCT

Он менее желателен, поскольку требует определения одного макроса для каждой структурной пары, тогда как первый определяет только один макрос для всей программы.Поскольку макросы не имеют пространства имен и, следовательно, могут плохо взаимодействовать, рекомендуется минимизировать их использование (если вообще невозможно их избежать).

Редактировать : Как уже было указановыход undef во втором растворе ограничивает загрязнение.

Кроме того, undef позволяет повторно использовать один и тот же макрос, не мешая другим именам макросов.

Это все еще несовершенно, поскольку некоторый другой код может полагаться на свой собственный независимый макрос MY_STRUCT, и использование нами MY_STRUCT может все еще сломать его, непреднамеренно переопределив и, позже, отменив определение.

2 голосов
/ 24 июня 2019

Я бы хотел использовать ту же структуру, иногда упакованную, иногда нет. Знаете ли вы, как написать это

Нет.

Как, черт возьми, вы ожидаете иметь два совершенно разных макета памяти, определенных одним и тем же кодом?

1 голос
/ 25 июня 2019

Вы можете использовать универсальные указатели на элементы для доступа к нему.

Сначала определите ваших членов в пространстве имен:

namespace universal {
  template<class T, unsigned int idx=0> struct member_ptr; // TODO
  template<auto const*...> struct packed_struct; // TODO
  template<auto const*...> struct unpacked_struct; // TODO
  template<class...Ts> using variant=std::variant<Ts...>;
  template<class...Ts> struct mutable_pointer:std::variant<Ts*...>{/*TODO*/};
  template<class...Ts> using const_pointer = mutable_ptr<Ts const...>;
}

namespace Foo {
  universal::member_ptr<int16_t> first;
  universal::member_ptr<int8_t> second;

  using packed = universal::packed_struct< &first, &second >;
  using unpacked = universal::unpacked_struct< &first, &second >;

  using either = universal::variant<packed, unpacked>;
  using either_cptr = universal::const_pointer<packed, unpacked>;
  using either_mptr = universal::mutable_pointer<packed, unpacked>;
}

тогда вы можете сделать:

void receivePacketFromNetwork( Foo::either_mptr ptr ) {
  assert(ptr);
  ptr->*Foo::first = 7;
  ptr->*Foo::second = 3;
}

и он работает на обоих типах структуры.

Запись материала в namespace universal не просто , но это не невозможно.

Основная идея - перегрузить operator->*.

template<class T>
struct member_ptr {
  template<class...Ts,
    std::enable_if_t< supports<Ts>() && ..., bool> = true
  >
  T& operator->*( std::variant<Ts...>& lhs, member_ptr const& self ) {
    return std::visit(
      [&self]( auto&& lhs )->T&{ return lhs->*self; },
      lhs
    );
  }
  template<class U>
  constexpr static bool supports(); //TODO
};

template<auto const* a, auto const* b, auto const*... bs>
struct unpacked_struct<a, b, bs...>:
  unpacked_struct<a>,
  unpacked_struct<b, bs...>
{
  using unpacked_struct<a>::operator->*;
  using unpacked_struct<b, bs...>::operator->*;
};

template<class T, , unsigned int idx, member_ptr<T, idx> const* a>
struct unpacked_struct<a> {
  T data;
  T& operator->*( member_ptr<T, idx> const& ) & {
    return data;
  }
  T&& operator->*( member_ptr<T, idx> const& ) && {
    return std::move(data);
  }
  T const& operator->*( member_ptr<T, idx> const& ) const& {
    return data;
  }
  T const&& operator->*( member_ptr<T, idx> const& ) const&& {
    return std::move(data);
  }
};

и т.д.

...