我有此代码:
// 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
答案 0 :(得分:2)
我想使用相同的结构,有时打包,有时不打包。你知道写这个的任何方式
没有。
您究竟期望如何用同一代码定义两个完全不同的内存布局?
答案 1 :(得分: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
的使用仍然可以通过无意中重新定义并在以后取消定义它来破坏它。
答案 2 :(得分: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);
}
};
等