Should I use bit fields?

Pages: 123
Hi

I read this on wikipedia:
bit members in structs have potential practical drawbacks. First, the ordering of bits in memory is cpu dependent and memory padding rules can vary between compilers. In addition, less well optimized compilers sometimes generate poor quality code for reading and writing bit members

http://en.wikipedia.org/wiki/Bit_field

Is this relevant? Should I be using something instead of a bit field?

I'm advocating using a bit field as in the following code snippet. I prefer this over std::bitset because each bit in a bit field has a name, rather than just a bit position (eg: if (mask.bits.symbol) ...)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    union mask_t
    {
        typedef uint16_t mask_sz_t;
        struct bits_t
        {
            mask_sz_t
                 inst_type     : 1, //  1. 0x0001: inst has a type
                 under         : 1, //  2. 0x0002: inst has an underlying instrument
                 currency      : 1, //  3. 0x0004: inst has a currency
                 exch          : 1, //  4. 0x0008: inst has a exchange
                 contract_size : 1, //  5. 0x0010: inst has a contract size
                 symbol        : 1, //  6. 0x0020: inst has a symbol
                 expiration    : 1, //  7. 0x0040: inst has an expiration
                 strike        : 1, //  8. 0x0080: inst has a strike
                 option_type   : 1, //  9. 0x0100: inst has an option type
                 exercise_type : 1, // 10. 0x0200: inst has an exercise type
                 version       : 1, // 11. 0x0400: inst has a version (stock splt etc)
                 spread_type   : 1; // 12. 0x0800: inst has a spread type
        } bits;
        mask_sz_t all;

        mask_t(mask_sz_t initial = 0) : all(initial) // set bits (defaults to 0)
        {
            assert(sizeof(mask_sz_t) >= sizeof(bits_t));
        }
    } mask;


I suppose I could have the following:

1
2
3
4
5
6
7
namespace inst_bits
{
    const uint16_t symbol = 0x0020;
}
std::bitset<uint16_t> mask;
if (mask.set(inst_bits::symbol)
    ....

I don't know... which is preferable?

Is it worthwhile me going back over my code which uses bit fields and changing them to bitsets?

TIA
Steve
It depends on where it's used. Are they packed into a message header, or is it an application construct?
Sorry for the delay in response...

It's in an application construct.

However, why is this relevant? What's the difference?

Thanks
Steve
The difference is if you want to send these properties at high speed across computer networks, then bit fields are the way to go. If it's in the application, it doesn't quite sound like the thing to do.
Last edited on
but internal to bitsets the storage is just an unsigned long. Sending the lower 16 bits of a bitset will be the same speed as sending a bitfield which is 16 bits wide.
I was answering the wrong question!

I was looking at your actual fields and got carried away with thoughts of something else ... namely, why aren't these virtual functions in some Instrument hierarchy.

Back to your question, I prefer const's rather than bitfields. I thought they were more portable, but after checking the standard, I can't find a basis for that position.
They are... sort of!

The instrument hierarchy is

instrument (abstract)

and concrete derivations

stock, index, future, option etc

However, the mask is stored in instrument base class. I think there is room for improvement!
Hmm, well, believe it or not but boost doesn't have a solution for this, so I cooked one up quickly.
It's a bit rough around the edges, but it is a start:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <boost/type_traits/is_same.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/vector.hpp>
#include <iostream>

template< typename Target, typename Source, size_t N >
struct find_bit;

namespace detail
{
    struct none_t {};

    template< typename Target, typename Source, size_t N, bool Found >
    struct find_bit_helper;

    template< typename Target, typename Source, size_t N >
    struct find_bit_helper< Target, Source, N, true >
        { enum { value = N }; };

    template< typename Target, typename Source, size_t N >
    struct find_bit_helper< Target, Source, N, false >
        { enum { value = find_bit< Target, Source, N + 1 >::position }; };
}

template< typename Target, typename Source, size_t N >
struct find_bit
{
    enum { position =
        detail::find_bit_helper< Target, Source, N, boost::is_same<
            Target, typename boost::mpl::at< Source,
            boost::mpl::integral_c< int, N > >::type >::value
        >::value
    };
};

template< typename B0  = detail::none_t, typename B1  = detail::none_t, typename B2  = detail::none_t,
          typename B3  = detail::none_t, typename B4  = detail::none_t, typename B5  = detail::none_t,
          typename B6  = detail::none_t, typename B7  = detail::none_t, typename B8  = detail::none_t,
          typename B9  = detail::none_t, typename B10 = detail::none_t, typename B11 = detail::none_t,
          typename B12 = detail::none_t, typename B13 = detail::none_t, typename B14 = detail::none_t,
          typename B15 = detail::none_t >
class bitfield
{
    uint32_t bits;

    typedef boost::mpl::vector< B0, B1, B2, B3, B4, B5, B6, B7, B8, B9,
        B10, B11, B12, B13, B14, B15 > bit_names;

  public:
    bitfield() : bits() {}

    template< typename Bit >
    void set() { bits |= ( 1 << find_bit<Bit, bit_names, 0>::position ); }

    template< typename Bit >
    void reset() { bits &= ~( 1 << find_bit<Bit, bit_names, 0>::position ); }

    template< typename Bit >
    void flip() { bits ^= ( 1 << find_bit<Bit, bit_names, 0>::position ); }

    template< typename Bit >
    bool get() { return !!( bits & ( 1 << find_bit<Bit, bit_names, 0>::position ) ); }
};


struct bit_zero {};
struct bit_one {};
struct bit_two {};
struct bit_three {};

int main() {
    bitfield< bit_zero, bit_one, bit_two, bit_three > bits;

    bits.set< bit_zero >();
    bits.set< bit_two >();
    bits.flip< bit_one >();

    std::cout << "bit_zero = " << bits.get<bit_zero>() << std::endl;
    std::cout << "bit_one = " << bits.get<bit_one>() << std::endl;
    std::cout << "bit_two = " << bits.get<bit_two>() << std::endl;
    std::cout << "bit_three = " << bits.get<bit_three>() << std::endl;
}


This allows you to name your bits using C++ types rather than constants. The nice thing about this approach
over the constant approach is you can't attempt to access a bit that doesn't exist in the bit field, as would be
the problem if you had two independent bitfields, each containing different bits, and two independent sets
of constants.

The above implementation is limited to 16 bits because my mpl vector is limited to 20 types, thus I chose
16.

EDIT: NB: Use of the named type concept as a bit name comes from boost::multi_index_container, which allows
for a similar thing with indexes (you can number them, or you can name them. But the above implementation
only supports names.)

EDIT2: The above implementation also solves the bit ordering problem: you control the order of the bits
directly, by virtue of the order of the names in the bitfield<> declaration. B0 is the least significant bit.
Last edited on
Interesting concept, but unfortunately not very useful when you want to get/set a bit which is determined by a variable's value.

1
2
int bit_to_set = 3;
std::cout << "bit_to_set = " << bits.get<bit_to_set>() << std::endl;


will obviously fail because this is an attempt to use a variable where a constant expression is required.

Similarly, processing something like this:

1
2
int received_mask = 0x1C;
// set bits associated with received_mask 


isn't currently supported.

You'd have to mix your strongly typed access with unsafe untyped access using a received integer value or whatever.
Interesting concept, but unfortunately not very useful when you want to get/set a bit which is determined by a variable's value.

You can add other member functions to the class, like :
void set(unsigned n) { bits |= ( 1 << n ); }
That, as OP said, degrades the value of the class because the class cannot, at compile time, ensure you are
setting a valid bit. It has to be a runtime check. I could write

1
2
template< size_t N >
void set() { /* ... */ }


and the associated functions and validate N at compile time.

However what OP wants is the ability to determine what bits to set in a particular line of code at runtime, and templates
don't give that ability: it has to be compile time.

We could bridge the gap a bit with the above member function and this code:

1
2
3
4
5
6
7
8
9
10
11
12
13
template< typename BitFieldType, typename InIter >
void set_bits( BitFieldType& bf, InIter first, InIter last )
{
    for( ; first != last; ++first )
        switch( *first )
        {
            case 0: bf.set<0>(); break;
            case 1: bf.set<1>(); break;
            /* ... */
            case 15: bf.set<15>(); break;
            default: throw std::range_error();
        }
}


Although that would break the change I was just about to make to the class - that change being that
you cannot call set<>, get<>, flip<>, or reset<>and specify a bit whose type is "none_t" since
presumably that bit does not exist. That would make the above function useless; you'd end up
writing 15 versions:

1
2
3
4
5
6
7
8
9
10
11
12
template< typename B0, typename InIter >
void set_bits( bitfield<B0, none_t, none_t, ... >& bf, InIter first, InIter last )
{
    for( ; first != last; ++first )
        switch( *first )
        {
            case 0: bf.set<0>(); break;
            default: throw std::range_error();
        }
}

// Then another function for B0 and B1 with case 0 and case 1, another function for B0, B1, and B2, etc, etc. 


This is an interesting thread, but original question remains unanswered, which was namely:

should I favour bitsets over bitfields and why?


The question was sparked when I read the following on wikipedia:
bit members in structs have potential practical drawbacks. First, the ordering of bits in memory is cpu dependent and memory padding rules can vary between compilers. In addition, less well optimized compilers sometimes generate poor quality code for reading and writing bit members


Breaking down the drawbacks listed above:

1. Ordering of bits is cpu dependent


When would this become an issue?

Does the order of bits in a bitfield differ to the order of bits in say, an unsigned integer?

if (mask.all & 0xC1) ... // relies on a certain ordering of bits.

Is this not safe with a bitfield?

2. memory padding rules can vary between compilers


Really? Surely it could be platform dependant? And surely, if you have memory set to align along the word boundary, you're going to have similar memory consumption and access statistics when using bitfields and bitsets?

3. less well optimized compilers sometimes generate poor quality code for reading and writing bit members


Which compilers? Does GCC generate poor code? How does the code generated differ to that of using a variable and a mask? eg:

1
2
int val;
if (val & 0x02) ... // 2nd bit is set 


I like bitfields because I have control over the size, whereas with bitsets you're limited to multiples of unsigned longs.
However, with no answers to my questions, I'm of the mindset that std::bitset is what I should go for instead.
Bit ordering would become an issue, for example, if you attempted to read a port, then overlay the value read
onto a bit field struct such as your bits_t. inst_type *might* get stored in the least significant bit of the allocated
short, or, it might get stored in the most significant bit.

I would favor std::bitset<> over the C-style bit fields only because C++ provides very little support for bit fields.
You do have control over the size with a bitset<>; that is what the N template parameter is. Yes, it just so happens
that bitset<> only allocates multiples of 4 bytes (assuming 32 bit), so you get 4 bytes when you only need 17 bits.
On the other hand, even with the C-style bitfield you'll end up wasting space (that's where the padding comes in--
the compiler may choose to align the next struct member on a word boundary, thus 11 bits are wasted).

Having said all that, I'm a big proponent of writing code that is as close to maintenance free as possible, and to
that end, I can understand why "const uint16_t symbol = 0x0001;" et al. is not the greatest of solutions. I prefer
code that is harder to get wrong, which is where my bitfield class came in.

Because of bitfield's built-in limitation of 16 bits maximum, you'll probably find it to be faster than bitset<>, since
most of my calculations can be done at compile time. Incidentally, here's a new version, that provides most of the
API methods that bitset<> provides (albeit some slightly modified):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <boost/mpl/at.hpp>
#include <boost/mpl/pop_front.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/utility/enable_if.hpp>
#include <iostream>  // EDIT: needed only for test code below

namespace detail
{
    struct none {};

    template< typename Target, typename Source, size_t N >
    struct find_bit;

    template< typename Target, typename Source, size_t N, bool Found >
    struct find_bit_helper;

    template< typename Target, typename Source, size_t N >
    struct find_bit_helper< Target, Source, N, true >
        { enum { value = N }; };

    template< typename Target, typename Source, size_t N >
    struct find_bit_helper< Target, Source, N, false >
        { enum { value = find_bit< Target, Source, N + 1 >::position }; };

    template< typename MplVector, size_t N >
    struct type_counter;

    template< typename MplVector, typename ThisType, size_t N >
    struct type_counter_helper
        { enum { count = 1 + type_counter< typename boost::mpl::pop_front<MplVector>::type, N + 1 >::count }; };

    template< typename MplVector, size_t N >
    struct type_counter_helper< MplVector, detail::none, N >
        { enum { count = 0 }; };

    template< typename MplVector, size_t N >
    struct type_counter
        { enum { count = type_counter_helper< MplVector, typename boost::mpl::at<
                        MplVector, typename boost::mpl::integral_c< int, 0 > >::type, N >::count };
        };

    template< typename MplVector >
    struct type_counter< MplVector, 16 >
        { enum { count = 0 }; };

    template< typename Target, typename Source, size_t N >
    struct find_bit
    {
        enum { position =
            detail::find_bit_helper< Target, Source, N, boost::is_same<
                Target, typename boost::mpl::at< Source,
                boost::mpl::integral_c< int, N > >::type >::value
            >::value
        };
    };
}
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
template< typename B0,                 typename B1  = detail::none, typename B2  = detail::none,
          typename B3  = detail::none, typename B4  = detail::none, typename B5  = detail::none,
          typename B6  = detail::none, typename B7  = detail::none, typename B8  = detail::none,
          typename B9  = detail::none, typename B10 = detail::none, typename B11 = detail::none,
          typename B12 = detail::none, typename B13 = detail::none, typename B14 = detail::none,
          typename B15 = detail::none >
class bitfield
{
    uint32_t bits;

    typedef boost::mpl::vector< B0, B1, B2, B3, B4, B5, B6, B7, B8, B9,
        B10, B11, B12, B13, B14, B15 > bit_names;

  public:
    typedef size_t size_type;

    bitfield() : bits() {}

    explicit bitfield( bool b0 )
        { set<0>( b0 ); }

    bitfield( bool b0, bool b1 )
        { set<0>( b0 ); set<1>( b1 ); }

    bitfield( bool b0, bool b1, bool b2 )
        { set<0>( b0 ); set<1>( b1 ); set<2>( b2 ); }

    bitfield( bool b0, bool b1, bool b2, bool b3 )
        { set<0>( b0 ); set<1>( b1 ); set<2>( b2 ); set<3>( b3 ); }

    bitfield( bool b0, bool b1, bool b2, bool b3, bool b4 )
        { set<0>( b0 ); set<1>( b1 ); set<2>( b2 ); set<3>( b3 ); set<4>( b4 ); }

    bitfield( bool b0, bool b1, bool b2, bool b3, bool b4, bool b5 )
        { set<0>( b0 ); set<1>( b1 ); set<2>( b2 ); set<3>( b3 ); set<4>( b4 ); set<5>( b5 ); }

    bitfield( bool b0, bool b1, bool b2, bool b3, bool b4, bool b5, bool b6 )
        { set<0>( b0 ); set<1>( b1 ); set<2>( b2 ); set<3>( b3 ); set<4>( b4 ); set<5>( b5 );
          set<6>( b6 );
        }

    bitfield( bool b0, bool b1, bool b2, bool b3, bool b4, bool b5, bool b6, bool b7 )
        { set<0>( b0 ); set<1>( b1 ); set<2>( b2 ); set<3>( b3 ); set<4>( b4 ); set<5>( b5 );
          set<6>( b6 ); set<7>( b7 );
        }

    bitfield( bool b0, bool b1, bool b2, bool b3, bool b4, bool b5, bool b6, bool b7, bool b8 )
        { set<0>( b0 ); set<1>( b1 ); set<2>( b2 ); set<3>( b3 ); set<4>( b4 ); set<5>( b5 );
          set<6>( b6 ); set<7>( b7 ); set<8>( b8 );
        }

    bitfield( bool b0, bool b1, bool b2, bool b3, bool b4, bool b5, bool b6, bool b7, bool b8,
        bool b9 )
        { set<0>( b0 ); set<1>( b1 ); set<2>( b2 ); set<3>( b3 ); set<4>( b4 ); set<5>( b5 );
          set<6>( b6 ); set<7>( b7 ); set<8>( b8 ); set<9>( b9 );
        }

    bitfield( bool b0, bool b1, bool b2, bool b3, bool b4, bool b5, bool b6, bool b7, bool b8,
        bool b9, bool b10 )
        { set<0>( b0 ); set<1>( b1 ); set<2>( b2 ); set<3>( b3 ); set<4>( b4 ); set<5>( b5 );
          set<6>( b6 ); set<7>( b7 ); set<8>( b8 ); set<9>( b9 ); set<10>( b10 );
        }

    bitfield( bool b0, bool b1, bool b2, bool b3, bool b4, bool b5, bool b6, bool b7, bool b8,
        bool b9, bool b10, bool b11 )
        { set<0>( b0 ); set<1>( b1 ); set<2>( b2 ); set<3>( b3 ); set<4>( b4 ); set<5>( b5 );
          set<6>( b6 ); set<7>( b7 ); set<8>( b8 ); set<9>( b9 ); set<10>( b10 ); set<11>( b11 );
        }

    bitfield( bool b0, bool b1, bool b2, bool b3, bool b4, bool b5, bool b6, bool b7, bool b8,
        bool b9, bool b10, bool b11, bool b12 )
        { set<0>( b0 ); set<1>( b1 ); set<2>( b2 ); set<3>( b3 ); set<4>( b4 ); set<5>( b5 );
          set<6>( b6 ); set<7>( b7 ); set<8>( b8 ); set<9>( b9 ); set<10>( b10 ); set<11>( b11 );
          set<12>( b12 );
        }

    bitfield( bool b0, bool b1, bool b2, bool b3, bool b4, bool b5, bool b6, bool b7, bool b8,
        bool b9, bool b10, bool b11, bool b12, bool b13 )
        { set<0>( b0 ); set<1>( b1 ); set<2>( b2 ); set<3>( b3 ); set<4>( b4 ); set<5>( b5 );
          set<6>( b6 ); set<7>( b7 ); set<8>( b8 ); set<9>( b9 ); set<10>( b10 ); set<11>( b11 );
          set<12>( b12 ); set<13>( b13 );
        }

    bitfield( bool b0, bool b1, bool b2, bool b3, bool b4, bool b5, bool b6, bool b7, bool b8,
        bool b9, bool b10, bool b11, bool b12, bool b13, bool b14 )
        { set<0>( b0 ); set<1>( b1 ); set<2>( b2 ); set<3>( b3 ); set<4>( b4 ); set<5>( b5 );
          set<6>( b6 ); set<7>( b7 ); set<8>( b8 ); set<9>( b9 ); set<10>( b10 ); set<11>( b11 );
          set<12>( b12 ); set<13>( b13 ); set<14>( b14 );
        }

    bitfield( bool b0, bool b1, bool b2, bool b3, bool b4, bool b5, bool b6, bool b7, bool b8,
        bool b9, bool b10, bool b11, bool b12, bool b13, bool b14, bool b15 )
        { set<0>( b0 ); set<1>( b1 ); set<2>( b2 ); set<3>( b3 ); set<4>( b4 ); set<5>( b5 );
          set<6>( b6 ); set<7>( b7 ); set<8>( b8 ); set<9>( b9 ); set<10>( b10 ); set<11>( b11 );
          set<12>( b12 ); set<13>( b13 ); set<14>( b14 ); set<15>( b15 );
        }

    template< typename Bit >
    typename boost::disable_if_c< boost::is_same< Bit, detail::none >::value, bitfield& >::type
    set()
        { bits |= ( 1 << detail::find_bit<Bit, bit_names, 0>::position ); return *this; }

    template< size_t N > bitfield& set()
        { return set< typename boost::mpl::at< bit_names, boost::mpl::integral_c< size_t, N > >::type >(); }

    template< typename Bit > bitfield& set( bool val )
        { return val ? set<Bit>() : reset<Bit>(); }

    template< size_t N > bitfield& set( bool val )
        { return val ? set<N>() : reset<N>(); }

    bitfield& set()
        { bits = ( 1 << size() ) - 1; return *this; }

    template< typename Bit >
    typename boost::disable_if_c< boost::is_same< Bit, detail::none >::value, bitfield& >::type
    reset()
        { bits &= ~( 1 << detail::find_bit<Bit, bit_names, 0>::position ); return *this; }

    template< size_t N > bitfield& reset()
        { return reset< typename boost::mpl::at< bit_names, boost::mpl::integral_c< size_t, N > >::type >(); }

    bitfield& reset() { bits = 0; return *this; }

    template< typename Bit >
    typename boost::disable_if_c< boost::is_same< Bit, detail::none >::value, bitfield& >::type
    flip()
        { bits ^= ( 1 << detail::find_bit<Bit, bit_names, 0>::position ); return *this; }

    template< size_t N > bitfield& flip()
        { return flip< typename boost::mpl::at< bit_names, boost::mpl::integral_c< size_t, N > >::type >(); }

    bitfield& flip()
        { bits = ( ~bits ) & ( ( 1 << size() ) - 1 ); return *this; }

    template< typename Bit >
    typename boost::disable_if_c< boost::is_same< Bit, detail::none >::value, bool >::type
    get() const
        { return bits & ( 1 << detail::find_bit<Bit, bit_names, 0>::position ); }

    template< size_t N > bool get() const
        { return get< typename boost::mpl::at< bit_names, boost::mpl::integral_c< size_t, N > >::type >(); }

    template< typename Bit > bool test() const
        { return get<Bit>(); }

    template< size_t N > bool test() const
        { return get<N>(); }


    bool any() const
        { return bits; }

    bool none() const
        { return !any(); }

    // Perhaps not the most efficient implementation.  Similar to GCC's though.
    size_type count() const
        {
            static const size_type bitsSetPerNibble[] =
                { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };

            size_type count = 0;
            for( size_t i = 0; i < 4; ++i )
                count += bitsSetPerNibble[ ( bits >> ( 8 * i ) ) & 0x0F ];
            return count;
        }

    unsigned long to_ulong() const { return bits; }

    bool operator==( const bitfield& rhs ) const
        { return bits == rhs.bits; }

    bool operator!=( const bitfield& rhs ) const
        { return !( *this == rhs ); }

    bitfield& operator&=( const bitfield& rhs )
        { bits &= rhs.bits; return *this; }

    bitfield& operator^=( const bitfield& rhs )
        { bits ^= rhs.bits; return *this; }

    bitfield& operator|=( const bitfield& rhs )
        { bits |= rhs.bits; return *this; }

    bitfield& operator>>=( size_t num )
        { bits >>= num; return *this; }

    bitfield& operator<<=( size_t num )
        { bits = ( bits << num ) & ( ( 1 << size() ) - 1 ); return *this; }

    size_type size() const
        { return detail::type_counter< bit_names, 0 >::count; }
};
Example code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct bit_zero {};
struct bit_one {};
struct bit_two {};
struct bit_three {};
struct bit_four {};

int main() {
    bitfield< bit_zero, bit_one, bit_two, bit_three, bit_four > bits;

    bits.set< bit_zero >();   // Bit 0 = 1
    bits.set< bit_two >();    // Bit 2 = 1
    bits.reset< bit_two >();  // Bit 2 = 0
    bits.flip< bit_one >();   // Bit 1 = 1
    bits.set<4>();            // Bit 4 = 1

    // Output should be 1, 1, 0, 0, 1
    std::cout << "bit_zero = " << bits.get<bit_zero>() << std::endl;
    std::cout << "bit_one = " << bits.get<bit_one>() << std::endl;
    std::cout << "bit_two = " << bits.get<bit_two>() << std::endl;
    std::cout << "bit_three = " << bits.get<bit_three>() << std::endl;
    std::cout << "bit_four = " << bits.get<bit_four>() << std::endl;
    std::cout << "bit count = " << bits.size() << std::endl;
}

EDIT: there is a bug in all of the above constructors except the default one in that each constructor needs a
: bits() in the initializer list.
That's very cool stuff - thanks.

However, in the event that I do want to read a port etc, how do I set the values efficiently; ie: without doing something like this:

1
2
3
4
5
6
7
const uint32_t bit_one_pos = 0x01;
const uint32_t bit_two_pos = 0x02;
const uint32_t bit_three_pos = 0x04;

if (read_bits & bit_one_pos) bits.set<bit_one >();
if (read_bits & bit_two_pos) bits.set<bit_two >();
if (read_bits & bit_three_pos) bits.set<bit_three >();


I suppose I could do something a little more elegant; perhaps like this:

1
2
3
4
5
6
7
8
struct bit_zero  { enum { pos = 0x00 }; };
struct bit_one   { enum { pos = 0x01 }; };
struct bit_two   { enum { pos = 0x02 }; };
struct bit_three { enum { pos = 0x04 }; };

if (read_bits & bit_one::pos)   bits.set< bit_one >();
if (read_bits & bit_two::pos)   bits.set< bit_two >();
if (read_bits & bit_three::pos) bits.set< bit_three >();


What would be the recommended way to set multiples of bits at the same time?
I've done this for setting individual bits...

Strongly typed specification of bits = your structs (bit_zero, et al)
Each type has an anonymous enum which specifies the bit position it relates to
Calculate the bitmask for each type at compile time (using bit_mask struct below), and pass the result of that & mask to set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
struct bit_zero  { enum { pos = 0 }; };
struct bit_one   { enum { pos = 1 }; };
struct bit_two   { enum { pos = 2 }; };
struct bit_three { enum { pos = 3 }; };
struct bit_four  { enum { pos = 4 }; };

template< int pos >
struct bit_mask { enum { value = 1 << pos }; };

int main()
{
    // create a bitset of 5 bits
    bitset< bit_zero, bit_one, bit_two, bit_three, bit_four > bits;

    uint8_t mask = 0x12; // 10010
    
    bits.set< bit_zero  >( mask & bit_mask< bit_zero::pos  >::value );
    bits.set< bit_one   >( mask & bit_mask< bit_one::pos   >::value );
    bits.set< bit_two   >( mask & bit_mask< bit_two::pos   >::value );
    bits.set< bit_three >( mask & bit_mask< bit_three::pos >::value );
    bits.set< bit_four  >( mask & bit_mask< bit_four::pos  >::value );

    std::cout << "bit_zero  = " << bits.get< bit_zero  >() << std::endl;
    std::cout << "bit_one   = " << bits.get< bit_one   >() << std::endl;
    std::cout << "bit_two   = " << bits.get< bit_two   >() << std::endl;
    std::cout << "bit_three = " << bits.get< bit_three >() << std::endl;
    std::cout << "bit_four  = " << bits.get< bit_four  >() << std::endl;
    std::cout << "size      = " << bits.size() << std::endl;

    return 0;
}
jsmith...

I implemented your code, and then came across a strange error.

In file included from ~/proj/sock/inc/rx_buf.h:5,
from ~/proj/rts/inc/rx_buf.h:12,
from ~/proj/rts/inc/client.h:4,
from src/client.cpp:1:
~/proj/utils/inc/bitset.h:98: error: expected nested-name-specifier before numeric constant
~/proj/utils/inc/bitset.h:98: error: expected ‘>’ before numeric constant
~/proj/utils/inc/bitset.h:111: error: ‘B1’ was not declared in this scope
~/proj/utils/inc/bitset.h:111: error: ‘B2’ was not declared in this scope
... etc etc etc...
~/proj/utils/inc/bitset.h:111: error: ‘B15’ was not declared in this scope
~/proj/utils/inc/bitset.h:111: error: type/value mismatch at argument 1 in template parameter list for ‘template<class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15, class T16, class T17, class T18, class T19> struct boost::mpl::vector’
~/proj/utils/inc/bitset.h:111: error: expected a type, got ‘0’
~/proj/utils/inc/bitset.h:111: error: template argument 2 is invalid
~/proj/utils/inc/bitset.h:111: error: template argument 3 is invalid
... etc etc etc...
~/proj/utils/inc/bitset.h:111: error: template argument 16 is invalid


I've traced that if I include my bitset.h file before boost/asio.hpp I don't get this error. If I include boost/asio.hpp first, then I get the above error!

I'm v confused...
What causes this error?
Do I have the choice of using your bitset or boost's asio classes, but not both?
Add these to the public interface of the class:

1
2
3
4
5
6
7
    template< typename Bit >
    static size_t bit_pos()
        { return detail::find_bit<Bit, bit_names, 0>::position; }

    template< typename Bit >
    static uint32_t mask()
        { return 1 << bit_pos<Bit>(); }


And then you don't need the enum inside the strong types; you can just use the above functions.
bit_pos() returns the bit position (0 = least significant bit), and mask() returns the bit mask used
to isolate the bit.


I'm not sure there really is a way to be much more efficient about setting multiple bits at once. If you want to set bits 2, 3, and 4
at once, you do:

1
2
3
my_bits.set<bit_two>();
my_bits.set<bit_three>();
my_bits.set<bit_four>();


which amounts to

1
2
3
bits |= 4;
bits |= 8;
bits |= 16;


because all other computations are done at compile time.

The only way to be faster is to write

 
bits |= ( 4 | 8 | 16 );


such that the right-hand side is evaluated at compile time.

If you really want it, we could write a whole slew of methods to do this. Eg:

1
2
3
4
5
6
7
template< typename Bit0, typename Bit1 >
bitfield& set()
    { bits |= mask<Bit0>() | mask<Bit1>(); return *this; }

template< typename Bit0, typename Bit1, typename Bit2 >
bitfield& set()
    { bits |= mask<Bit0>() | mask<Bit1>() | mask<Bit2>(); return *this; }


etc, etc, all the way up to 20 template parameters. Then repeat for reset() and flip().


Can you post bitset.h so I know what lines your line numbers map to? One solution may be to put bitfield in a namespace,
but the strange thing is that the only symbol I have in the global namespace is bitfield<> itself. Everything else is scoped
either within the detail namespace or within bitfield itself.
Pages: 123