1

我有这个代码:

// 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
4

3 回答 3

2

我想使用相同的结构,有时是打包的,有时不是。你知道写这个的任何方法吗

没有。

您到底如何期望由相同的代码定义两种完全不同的内存布局?

于 2019-06-24T14:02:19.610 回答
2

我能想到的唯一方法是使用丑陋的宏:

#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仍然可以通过不经意地重新定义并随后取消定义来破坏它。

于 2019-06-24T14:44:14.763 回答
1

您可以使用通用成员指针来访问它。

首先,在命名空间中定义您的成员:

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);
  }
};

等等

于 2019-06-25T17:40:49.307 回答