Definition of bit fields according to specification at compile time

Suppose I have the following 2 structures based on 2 bytes:

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

And I have a union that contains them:

union Baz {
    unsigned short val;
    struct Foo foo;
    struct Bar bar;
} ;

And then in my program, I can put the value using val and get the values ​​a, b, c, d and e according to their bit fields, without bitwise operations / interfaces and more necessary.

The problem is that I need it to support both large and small endianness, which means that I need my structure to determine the bit fields according to the content at compile time.

So I need something like this:

#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. , , boosted.hpp , , , - .

?


edit1: : ++ 03, ++ 11/14 .

+4
2

: " ", , , : " ".

, Posix, C/++ . , , , , enddiannes.

+1

, sth 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
0

Source: https://habr.com/ru/post/1616794/


All Articles