在编译时根据字节顺序定义位域

时间:2015-11-20 19:34:33

标签: c++ endianness

假设我有以下2个基于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值,不需要按位操作/接口等。

问题是,我需要它支持大端和小端,这意味着我需要让我的struct根据编译时的字节顺序定义位域。

因此,我需要这样的事情:

#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,LITTLE_ORDER,BIG_ORDER等,但我不能确保它们将在请求的部署环境中定义,以及endian.h头文件。另外,据我所知,boost的endian.hpp正在实现我上面提到的关于宏的内容,所以我不确定它会有什么不同......

有什么建议吗?

EDIT1: 回答其中一条评论:我需要一个c ++ 03解决方案,但c ++ 11/14也可以用于启蒙。

2 个答案:

答案 0 :(得分:1)

问题对于一个简单的简短问题来说太长了:'我如何在编译期间知道endiannes。'并且遗憾的是,这个问题的答案是'你不能' ;

问题是,Posix和C / C ++标准都没有指定关于字节序的任何内容。您唯一能做的就是测试已知的特定于体系结构的宏并从中派生出enddiannes。

答案 1 :(得分:0)

正如我在评论中所说的那样,人们可以用std::bitset

来激动人心 然而,它有点冗长。

我还不能在大端机器上测试它,因此如果某人有这样的处理并发现了一个错误,我会很感激消息。

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

live在Coliru的

输出:

b105 f00d