在编译时根据字节序定义位字段
Defining bitfields according to endianness at compile time
假设我有以下两个基于2个字节的结构:
#pragma pack(1)
struct Foo {
unsigned short a : 5;
unsigned short b : 4;
unsigned short c : 2;
unsigned short d : 5;
} ;
struct Bar {
unsigned short a : 5;
unsigned short b : 4;
unsigned short c : 2;
unsigned short d : 3;
unsigned short e : 2;
} ;
我有一个包含他们的联盟:
union Baz {
unsigned short val;
struct Foo foo;
struct Bar bar;
} ;
然后在我的程序中,我可以使用val设置一个值,并根据它们的位字段获得a、b、c、d和e值,无需按位操作/接口等。
然而,问题是我需要它同时支持大字节序和小字节序,这意味着我需要让我的结构在编译时根据字节序定义位字段。
因此,我需要这样的东西:
#pragma pack(1)
#if BIG_ENDIAN
struct Foo {
unsigned short a : 5;
unsigned short b : 4;
unsigned short c : 2;
unsigned short d : 5;
} ;
struct Bar {
unsigned short a : 5;
unsigned short b : 4;
unsigned short c : 2;
unsigned short d : 3;
unsigned short e : 2;
} ;
#else
struct Foo {
unsigned short d : 5;
unsigned short c : 2;
unsigned short b : 4;
unsigned short a : 5;
} ;
struct Bar {
unsigned short e : 2;
unsigned short d : 3;
unsigned short c : 2;
unsigned short b : 4;
unsigned short a : 5;
} ;
#endif
我试着查找它,我能找到的要么是运行时检查,要么是只能在运行时使用的编译值。我知道有很多宏,如BYTE_ORDER、LITTLEORDER、BIG_ORDER等,但我不能确保它们会在请求的部署环境中以及endian.h头文件中定义。此外,据我所知,boost的endian.hpp正在实现我上面所说的关于宏的内容,所以我不确定它会有什么不同。。
有什么建议吗?
第1版:在回复其中一条评论时:我需要一个c++03解决方案,但一个c++11/14解决方案对启蒙也很好。
对于一个简单的简短问题来说,这个问题太长了:"我如何在编译时知道endiannes。",遗憾的是,这个问题的答案是"你不能"。
问题是,无论是Posix还是C/C++标准,都没有指定任何关于endianness的内容。你唯一能做的就是测试已知的特定于体系结构的宏,并从中派生enddiannes
正如我在评论中所说,一个人可以用std::bitset
煽动某事
然而,它有点冗长。
我还不能在big-endian机器上测试它,因此,如果有人可以使用它并发现一个bug,我将不胜感激
#include <iostream>
#include <iomanip>
#include <bitset>
#include <endian.h>
using std::size_t;
struct BE {};
struct LE {};
#if __BYTE_ORDER == __BIG_ENDIAN
using PE = BE;
#else
using PE = LE;
#endif
template<typename> struct BitTraits{};
template<> struct BitTraits<uint8_t> {
constexpr static size_t numbits = 8;
};
template<> struct BitTraits<uint16_t> {
constexpr static size_t numbits = 16;
};
template<typename T>
class BitField {
public:
using assignment_type = T;
using container_type =
typename std::bitset<BitTraits<T>::numbits>;
inline BitField(assignment_type data) : data_(data) {}
inline BitField& operator=(assignment_type data) {
data_ = data;
return *this;
}
inline container_type& data() {
return data_;
}
inline unsigned long long to_ullong() const {
return data_.to_ullong();
}
private:
container_type data_;
};
template<size_t, typename Endianness = PE> struct BitSliceTraits;
template<size_t length>
struct BitSliceTraits<
length,
typename std::enable_if<1<=length && length<=8, PE>::type
> {
using assignment_type = uint8_t;
inline static assignment_type little_endian(assignment_type b) {
return b;
}
};
template<size_t length>
struct BitSliceTraits<
length,
typename std::enable_if<9<=length && length<=16, LE>::type
> {
using assignment_type = uint16_t;
inline static assignment_type little_endian(assignment_type b) {
return b;
}
};
template<size_t length>
struct BitSliceTraits<
length,
typename std::enable_if<9<=length && length<=16, BE>::type
> {
using assignment_type = uint16_t;
inline static assignment_type little_endian(assignment_type b) {
assignment_type rv = (b & 0xFF) >> 8;
rv |= (b & 0xFF00) << 8;
return rv;
}
};
template<class B, size_t start, size_t end>
struct BitSlice {
static constexpr size_t length = end - start + 1;
using assignment_type =
typename BitSliceTraits<length>::assignment_type;
using ref_type = typename B::container_type;
inline explicit BitSlice(B& bf) noexcept : ref(bf.data()) {}
inline BitSlice& operator= (assignment_type arg) noexcept {
arg = BitSliceTraits<length>::little_endian(arg);
for (size_t i = start; i <= end; ++i) {
ref[i] = (arg & (1 << (i - start))) != 0 ? true : false;
}
return *this;
}
inline operator assignment_type () const noexcept {
assignment_type rv(0);
for (size_t i = start; i <= end; ++i) {
rv |= (assignment_type(ref[i]) << (i - start));
}
return rv;
}
ref_type& ref;
};
template<typename B>
struct BitFieldView {
using bitfield_type = B;
};
struct FooView : public BitFieldView<BitField<unsigned short>> {
using bft = bitfield_type;
inline explicit FooView (bft& bf) noexcept : a(bf), b(bf), c(bf) {}
BitSlice<bft, 0, 11> a; // 12
BitSlice<bft, 12, 12> b; // 1
BitSlice<bft, 13, 15> c; // 3
};
struct BarView : public BitFieldView<BitField<unsigned short>> {
using bft = bitfield_type;
inline explicit BarView (bft& bf) noexcept : a(bf), b(bf), c(bf), d(bf) {}
BitSlice<bft, 0, 3> a; //4
BitSlice<bft, 4, 7> b; //4
BitSlice<bft, 8, 11> c; //4
BitSlice<bft, 12, 15> d; //4
};
std::ostream& operator<<(std::ostream& os, uint8_t val) {
return os << std::hex << ulong(val);
}
int main(){
// BIOS FOOD
// 0xB105 0xF00D
{
BitField<unsigned short> bf = 0x0;
// 0xB105 = 1011 0001 0000 0101 = 101 1 000100000101;
FooView(bf).a = 0b000100000101;
FooView(bf).b = 1;
FooView(bf).c = 0b101;
std::cout << std::hex << bf.to_ullong() << ' ';
}
{
// 0xF00D
BitField<unsigned short> bf = 0b1111000000001101;
std::cout << BarView(bf).d
<< BarView(bf).c
<< BarView(bf).b
<< BarView(bf).a
<< std::endl;
}
return EXIT_SUCCESS;
}
住在Coliru's
输出:
b105 f00d
相关文章:
- C++:用户定义的类,以成员字段作为地址
- 使用 Boost::Hana 中的 BOOST_HANA_DEFINE_STRUCT 定义具有 40 个以上字段的结构
- 在类中定义数组的方法和字段
- 将字段(在类中)定义为引用的缺点是什么?
- 如何在消息文件中获取用户定义的字段
- 使用谷歌协议缓冲区 - 如何在.proto文件中定义字段
- 为什么使用最大位字段序列定义C++内存?
- 定义静态类字段时多个已定义符号的链接器错误
- SSE错误 - 使用M128I_I32定义__M128i变量的字段
- 为什么在未由语言本身定义的结构字节中的位字段顺序
- 使用 boost::geometry::追加时,自定义点类中的 ID 字段间歇性丢失
- 在 C++ 中填充重复的自定义协议缓冲区字段
- 正在取消引用自定义结构字段的迭代器
- 实现现有的网络接口,以相对于C 11中的Endianess定义位字段
- 类定义错误 - 字段下一个具有不完整的类型
- 谷歌协议缓冲区,如何设置自定义类型的字段
- 将具有默认值的构造函数定义为组合对象的私有字段
- 按一个字段对自定义对象向量进行排序
- C++ - 有没有办法将字段\方法添加到使用"typedef"定义的类型?
- 在实例变量中定义字段会使所有实例字段相同