9 thg 11, 2010

Should I use bit fields?



steve lorimer (64)  Link to this post Aug 9, 2010 at 7:18pm
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
kbw (2361)  Link to this post Aug 9, 2010 at 8:06pm
It depends on where it's used. Are they packed into a message header, or is it an application construct?
steve lorimer (64)  Link to this post Aug 16, 2010 at 6:52pm
Sorry for the delay in response...

It's in an application construct.

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

Thanks
Steve
kbw (2361)  Link to this post Aug 16, 2010 at 7:22pm
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 Aug 16, 2010 at 7:22pm
steve lorimer (64)  Link to this post Aug 16, 2010 at 7:28pm
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.
kbw (2361)  Link to this post Aug 16, 2010 at 8:38pm
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.
steve lorimer (64)  Link to this post Aug 16, 2010 at 8:56pm
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!
jsmith (4898)  Link to this post Aug 17, 2010 at 4:30am
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 Aug 17, 2010 at 4:33am
steve lorimer (64)  Link to this post Aug 17, 2010 at 1:32pm
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.
R0mai (608)  Link to this post Aug 17, 2010 at 4:32pm
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 ); }
jsmith (4898)  Link to this post Aug 17, 2010 at 7:52pm
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. 

steve lorimer (64)  Link to this post Aug 17, 2010 at 8:12pm
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.
jsmith (4898)  Link to this post Aug 17, 2010 at 9:39pm
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 Aug 17, 2010 at 9:41pm
jsmith (4898)  Link to this post Aug 17, 2010 at 9:40pm
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; }
};
jsmith (4898)  Link to this post Aug 17, 2010 at 9:40pm
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;
}
jsmith (4898)  Link to this post Aug 18, 2010 at 2:16am
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.
steve lorimer (64)  Link to this post Aug 18, 2010 at 1:45pm
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?
steve lorimer (64)  Link to this post Aug 18, 2010 at 5:43pm
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;
}
steve lorimer (64)  Link to this post Aug 18, 2010 at 7:59pm
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?
jsmith (4898)  Link to this post Aug 18, 2010 at 8:34pm
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.



There are 3 files:

http://www.mediafire.com/file/16fvmf051l07u6h/bitset.h
http://www.mediafire.com/file/81b547kgb2b7m4y/bitset_detail.h
http://www.mediafire.com/file/7vz6bj02zcn77yb/bitset_test.cpp

I'll post the source here also... please forgive the changes to types/names etc - I was banging my head against a wall trying to see if it was name conflicts - although how it could be when in a unique namespace I don't know!

bitset_detail:

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
#ifndef _typed_bitset_detail_h_
#define _typed_bitset_detail_h_

#include <boost/mpl/vector.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/pop_front.hpp>
#include <boost/type_traits/is_same.hpp>

namespace cmt { namespace bitset { namespace detail {

struct no_bit {};

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, no_bit, 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 };
};

//-------------------------------------------------------------------------------------

} // namespace detail
} // namespace bitset
} // namespace cmt

#endif 

steve lorimer (64)  Link to this post Aug 18, 2010 at 8:55pm
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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
#ifndef _typed_bitset_h_
#define _typed_bitset_h_

#include <stdint.h>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_same.hpp>
#include <utils/inc/bitset_detail.h>

namespace cmt { namespace bitset {

// compile-time calculation of the number of bits_ an integer requires
template<int cur>
struct num_bits { enum { value = 1 + num_bits<(cur >> 1)>::value }; };
template<> // exit condition 
struct num_bits<0> { enum { value = 0 }; };

// compile-time calculation of the bitmask relating to a given bit position
template< int pos >
struct bit_mask { enum { value = 1 << pos }; };

// provides a strongly typed bits of up to 16 bits_
template< typename B0,                   typename B1  = detail::no_bit, typename B2  = detail::no_bit,
          typename B3  = detail::no_bit, typename B4  = detail::no_bit, typename B5  = detail::no_bit,
          typename B6  = detail::no_bit, typename B7  = detail::no_bit, typename B8  = detail::no_bit,
          typename B9  = detail::no_bit, typename B10 = detail::no_bit, typename B11 = detail::no_bit,
          typename B12 = detail::no_bit, typename B13 = detail::no_bit, typename B14 = detail::no_bit,
          typename B15 = detail::no_bit >
class bits
{
public:
    typedef uint16_t bits_sz_t;
private:
    bits_sz_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;

    bits()
        : bits_()
    {}

    explicit bits(bool b0) : bits_()
    { set<0>(b0); }

    bits(bool b0, bool b1) : bits_()
    { set<0>(b0); set<1>(b1); }

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

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

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

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

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

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

    bits(bool b0, bool b1, bool b2, bool b3, bool b4, bool b5, bool b6, bool b7, bool b8) : bits_()
    { 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); }

    bits(bool b0, bool b1, bool b2, bool b3, bool b4, bool b5, bool b6, bool b7, bool b8, bool b9) : bits_()
    { 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); }

    bits(bool b0, bool b1, bool b2, bool b3, bool b4, bool b5, bool b6, bool b7, bool b8, bool b9, bool b10) : bits_()
    { 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); }

    bits(bool b0, bool b1, bool b2, bool b3, bool b4, bool b5, bool b6, bool b7, bool b8, bool b9, bool b10, bool b11) : bits_()
    { 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); }

    bits(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) : bits_()
    { 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); }

    bits(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) : bits_()
    { 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); }

    bits(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) : bits_()
    { 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); }

    bits(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) : bits_()
    { 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::no_bit >::value, bits& >::type set()
    {
        bits_ |= (1 << detail::find_bit<Bit, bit_names, 0>::position);
        return *this;
    }

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

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

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

    // turn on all bits
    bits& set()
    {
        bits_ = (1 << size()) - 1;
        return *this;
    }
    
    bits& set_mask(const bits_sz_t mask)
    {
        bits_ = mask;
        return *this;
    }
    
    template< typename Bit >
    typename boost::disable_if_c< boost::is_same< Bit, detail::no_bit >::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>();
    }

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

    // turn off bit at position N
    template< size_t N >
    bits& reset()
    {
        return reset< typename boost::mpl::at< bit_names, boost::mpl::integral_c< size_t, N > >::type >();
    }

    // turn off all bits_
    bits& reset()
    {
        bits_ = 0;
        return *this;
    }

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

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

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

    // true if any bits are set
    bool any() const
    {
        return bits_;
    }

    // true if no bits_ are set
    bool none() const
    {
        return !any();
    }

    size_type count() const
    {
        // Perhaps not the most efficient implementation.  Similar to GCC's though.
        static const size_type bits_SetPerNibble[] = { 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 += bits_SetPerNibble[ (bits_ >> (8 * i)) & 0x0F ];
        }
        return count;
    }

    unsigned long to_ulong() const
    {
        return bits_;
    }

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

} // namespace bitset
} // namespace cmt

#endif 
steve lorimer (64)  Link to this post Aug 18, 2010 at 8:56pm
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
//#include <boost/asio.hpp> // uncomment this line to have the compile fail
#include <utils/inc/bitset.h>
#include <iostream>

using namespace cmt::bitset;

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

int main()
{
    // create a bits of 5 bits
    bits< 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 (4898)  Link to this post Aug 18, 2010 at 9:28pm
EDIT: Actually, I can see the use of set() methods that can set multiple bits at once. For example, if the class' "bits" member was a memory-mapped port, then it would be useful to be able to set or clear or alter the state of multiple bits simultaneously. But
my above lines are wrong... they need disable_if's. Ugh. Here's a corrected example.

First, change the above from functions to enums. Not sure if the compiler will/can elide the function calls, and there is
no need for any of them to be functions anyway.

1
2
3
4
5
6
7
8
9
10
11
  public:
    template< typename Bit >
    struct bit_pos
        { enum { value = detail::find_bit<Bit, bit_names, 0>::position }; };

    template< typename Bit >
    struct mask
        { enum { value = 1 << bit_pos<Bit>::value }; };

  private:
        enum { full_mask = 1 << detail::type_counter< bit_names, 0 >::count };


Next, here is the corrected set() declaration:

1
2
3
4
5
template< typename Bit0, typename Bit1 >
typename boost::disable_if_c< boost::is_same< Bit0, detail::none >::value ||
    boost::is_same< Bit1, detail::none >::value, bitfield& >::type
    set()
    { bits |= mask<Bit0>::value | mask<Bit1>::value; return *this; }
jsmith (4898)  Link to this post Aug 18, 2010 at 9:34pm
Here is a corrected (and modified) count() function. The old one had a bug.
It shifted 8 bits at a time instead of 4.

1
2
3
4
5
6
7
8
9
10
11
    // 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 };

            return bitsSetPerNibble[ bits & 0x0F ] +
                bitsSetPerNibble[ ( bits >> 4 ) & 0x0F ] +
                bitsSetPerNibble[ ( bits >> 8 ) & 0x0F ] +
                bitsSetPerNibble[ ( bits >> 12 ) & 0x0F ];
        }
jsmith (4898)  Link to this post Aug 18, 2010 at 9:41pm
Can you repost the compile errors with the exact source files you posted?

Unfortunately my boost version predates asio, so I cannot compile your code :(
steve lorimer (64)  Link to this post Aug 18, 2010 at 9:52pm
Using this post: http://www.cplusplus.com/forum/general/27147/page2.html#msg147084

~/proj/utils/inc/bitset.h:22: error: expected nested-name-specifier before numeric constant
~/proj/utils/inc/bitset.h:22: error: expected ‘>’ before numeric constant


1
2
3
4
5
6
7
8
/* next line is line 22: */ 
template< typename B0,                   typename B1  = detail::no_bit, typename B2  = detail::no_bit,
          typename B3  = detail::no_bit, typename B4  = detail::no_bit, typename B5  = detail::no_bit,
          typename B6  = detail::no_bit, typename B7  = detail::no_bit, typename B8  = detail::no_bit,
          typename B9  = detail::no_bit, typename B10 = detail::no_bit, typename B11 = detail::no_bit,
          typename B12 = detail::no_bit, typename B13 = detail::no_bit, typename B14 = detail::no_bit,
          typename B15 = detail::no_bit >
class bits



~/proj/utils/inc/bitset.h:35: error: ‘B1’ was not declared in this scope
~/proj/utils/inc/bitset.h:35: error: ‘B2’ was not declared in this scope
... etc etc etc...
~/proj/utils/inc/bitset.h:35: error: ‘B15’ was not declared in this scope
~/proj/utils/inc/bitset.h:35: 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:35: error: expected a type, got ‘0’
~/proj/utils/inc/bitset.h:35: error: template argument 2 is invalid
~/proj/utils/inc/bitset.h:35: error: template argument 3 is invalid
... etc etc etc...
~/proj/utils/inc/bitset.h:35: error: template argument 16 is invalid


1
2
/* next line is line 35 */
typedef boost::mpl::vector< B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13, B14, B15 > bit_names;
jsmith (4898)  Link to this post Aug 18, 2010 at 10:20pm
Can you try renaming my template parameters from B0 etc to something else like BIT0? Looks kinda like boost.asio might
have already defined the symbol B0, perhaps as a macro?
steve lorimer (64)  Link to this post Aug 18, 2010 at 10:25pm
Aaargh - that is exactly it! I'm a dumbass! :(
jsmith (4898)  Link to this post Aug 18, 2010 at 11:12pm
That's annoying that asio would define those symbols in the global namespace.
steve lorimer (64)  Link to this post Aug 18, 2010 at 11:12pm
I extended the mask struct to allow creation of a mask from multiple bit types:


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
    template< typename Bit0,                   typename Bit1  = detail::no_bit, typename Bit2  = detail::no_bit,
              typename Bit3  = detail::no_bit, typename Bit4  = detail::no_bit, typename Bit5  = detail::no_bit,
              typename Bit6  = detail::no_bit, typename Bit7  = detail::no_bit, typename Bit8  = detail::no_bit,
              typename Bit9  = detail::no_bit, typename Bit10 = detail::no_bit, typename Bit11 = detail::no_bit,
              typename Bit12 = detail::no_bit, typename Bit13 = detail::no_bit, typename Bit14 = detail::no_bit,
              typename Bit15 = detail::no_bit >
    struct mask
    {
        enum { value = ((boost::is_same< Bit0,  detail::no_bit >::value == true ? 0 : 1) << bit_pos<Bit0 >::pos) |
                       ((boost::is_same< Bit1,  detail::no_bit >::value == true ? 0 : 1) << bit_pos<Bit1 >::pos) |
                       ((boost::is_same< Bit2,  detail::no_bit >::value == true ? 0 : 1) << bit_pos<Bit2 >::pos) |
                       ((boost::is_same< Bit3,  detail::no_bit >::value == true ? 0 : 1) << bit_pos<Bit3 >::pos) |
                       ((boost::is_same< Bit4,  detail::no_bit >::value == true ? 0 : 1) << bit_pos<Bit4 >::pos) |
                       ((boost::is_same< Bit5,  detail::no_bit >::value == true ? 0 : 1) << bit_pos<Bit5 >::pos) |
                       ((boost::is_same< Bit6,  detail::no_bit >::value == true ? 0 : 1) << bit_pos<Bit6 >::pos) |
                       ((boost::is_same< Bit7,  detail::no_bit >::value == true ? 0 : 1) << bit_pos<Bit7 >::pos) |
                       ((boost::is_same< Bit8,  detail::no_bit >::value == true ? 0 : 1) << bit_pos<Bit8 >::pos) |
                       ((boost::is_same< Bit9,  detail::no_bit >::value == true ? 0 : 1) << bit_pos<Bit9 >::pos) |
                       ((boost::is_same< Bit10, detail::no_bit >::value == true ? 0 : 1) << bit_pos<Bit10>::pos) |
                       ((boost::is_same< Bit11, detail::no_bit >::value == true ? 0 : 1) << bit_pos<Bit11>::pos) |
                       ((boost::is_same< Bit12, detail::no_bit >::value == true ? 0 : 1) << bit_pos<Bit12>::pos) |
                       ((boost::is_same< Bit13, detail::no_bit >::value == true ? 0 : 1) << bit_pos<Bit13>::pos) |
                       ((boost::is_same< Bit14, detail::no_bit >::value == true ? 0 : 1) << bit_pos<Bit14>::pos) |
                       ((boost::is_same< Bit15, detail::no_bit >::value == true ? 0 : 1) << bit_pos<Bit15>::pos) };
    };
steve lorimer (64)  Link to this post Aug 25, 2010 at 8:13pm
Do you know of a decent way to convert the types into strings, and then be able to get either a list of strings, or a long concatenated string, of the bits which are set and/or not set?

eg, something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
namespace my_mask {

struct bit1 {};
struct bit2 {};
struct bit3 {};

typedef bitset::bits<bit1, bit2, bit3> bits_t;

} // namespace my_mask

my_mask::bits_t mask;

mask.set<bit1>();
std::string str1 = mask.get_unset_str(); // returns "bit2, bit3"

mask.set<bit3>();
std::string str2 = mask.get_set_str(); // returns "bit1, bit3" 


TIA
Steve
Last edited on Aug 25, 2010 at 8:14pm
jsmith (4898)  Link to this post Aug 25, 2010 at 11:06pm
In namespace detail, add:

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
    template< typename BitName >
    struct bit_name
    {
        static std::string value( bool v )
            { return v ? ( typeid( BitName ).name() + std::string( " " ) ) : std::string(); }
    };

    template<>
    struct bit_name< none >
    {
        static std::string value( bool ) { return std::string(); }
    };

    template< typename MplVector, size_t N >
    struct stringize
    {
        static std::string as_str( uint32_t bits )
            {
              return bit_name< typename boost::mpl::at< MplVector,
                boost::mpl::integral_c< int, N > >::type >::value( bits & 1 ) +
                stringize< MplVector, N + 1 >::as_str( bits >> 1 );
            }
    };

    template< typename MplVector >
    struct stringize< MplVector, 16 >
    {
        static std::string as_str( uint16_t )
            { return std::string(); }
    };


In class bitfield, add the public member:

1
2
    std::string to_string() const
        { return detail::stringize<bit_names, 0>::as_str( bits ); }


Note: I'm just returning typeid( BitName ).name(), which returns the mangled name. You might consider running
the name through a demangler first. [On gcc, it mangles it by prepending an integer corresponding to the length
of the type, in characters. So for example, the type "bit_one" gets mangled as "7bit_one". I believe there is
a function called __cxa_demangle in cxxabi.h that you might be able to use.]
steve lorimer (64)  Link to this post Aug 25, 2010 at 11:07pm
Thanks once again guru! :)
steve lorimer (64)  Link to this post Aug 26, 2010 at 5:03pm
jsmith, if I may avail myself upon thee again please!

I'm trying to use your bitset in conjunction with a serialisable message.

Here is some code which I'm trying to use for creating my messages:

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
template<typename bitset_t>
struct msg_t
{
    template<typename T, typename field_t>
    class field
    {
        T data;
        bitset_t &mask;
        bitset_t &changes;

    public:
        field(bitset_t &m, bitset_t &c) : data(), mask(m), changes(c) { }

        operator       T&()       { return data; }
        operator const T&() const { return data; }

        template<typename U>
        field& operator=(const U &d)
        {
            mask.set<field_t>();
            changes.set<field_t>();
            data = d;
            return *this;
        }

        bool set()     const { return mask.get<field_t>(); }
        bool changed() const { return changes.get<field_t>(); }
    };
    //--------------------------------------

    bitset_t mask;
    bitset_t changes;
    
protected:
    msg_t() {}
};


Now in my client code, I create a message as such:

1
2
3
4
5
6
7
8
9
10
11
12
struct field0 {};
struct field2 {};

typedef bitset::bits<field0, field1> mask_t;

struct my_msg : public msg_t<mask_t>
{
    msg_t<mask_t>::field<int, field0> field_0;
    msg_t<mask_t>::field<std::string, field1> field_1;

    my_msg() : field_0(mask, changes), field_1(mask, changes) {} 
};


And if I now want to use my message, I can do the following:

1
2
3
my_msg m;
m.field_0 = 5;
m.field_1 = "hello world";


under the hood, msg_t<bitset_t>::field<int, field0>::operator=(U& d) should set the correct bit in both bitsets mask and changes.

However, even without attempting to instantiate the msg_t template class above (ie: without declaring struct my_msg, compiling fails with the following error:

line 20: error: expected primary-expression before '>' token

Any line where I attempt to use the bitset members fails. Since bitset is a template parameter, I would have thought the compiler would only try to parse it when the template is instantiated.

Is what I'm trying to do possible? Does it make sense?

TIA
Steve
jsmith (4898)  Link to this post Aug 26, 2010 at 8:44pm
Yes, it is possible and it makes sense. And even better, there's an easy answer.

You just have to add the keyword "template" at the right places:

1
2
3
// Lines 20 and 21 above.  You'll have to make similar change on lines 26 and 27.
mask.template set<field_t>();
changes.template set<field_t>();
Last edited on Aug 26, 2010 at 8:46pm
steve lorimer (64)  Link to this post Aug 26, 2010 at 8:56pm
Wah?!!? what on earth does that mean?! I've never seen syntax like that before?!

Why does the compiler need template there?

Just when I feel I'm getting a grasp on things, the old curve-ball comes along! :)

Thanks once again!
steve lorimer (64)  Link to this post Aug 26, 2010 at 9:23pm
I'm wondering if Loki's GenScatterHierarchy or something like that would make more sense for what I'm trying to do?
jsmith (4898)  Link to this post Aug 27, 2010 at 1:18am
So I looked up in the current C++ standard (n3092). I could quote the text here, but it probably wouldn't help. Anyway,
the section is 14.2 paragraph 4. Basically, the compiler needs it there because the standard says the compiler needs it
there.
steve lorimer (64)  Link to this post Aug 27, 2010 at 4:08pm
Thanks again - I read up the relevant section - so it's to tell the compiler not to parse the first angle bracket as less-than, but rather as a template argument list.

I have another question relating to this. The below code snippet is slightly different to the previous snippet I posted, but the basic gist is the same.

Given a template class field, which has a member bitset mask (whose type is template param bitset_t), and another template param field_t which specifies the bit position in the bitset, how can I use bitset's local structbit_pos<field_t>::pos to find the bit position in the bitset?

See code below:

1
2
3
4
5
template<typename data_t, typename field_t, typename bitset_t>
inline object& operator&(const field<data_t, field_t, bitset_t, send_changes> &data)
{    
    std::cout << "mask pos=" << data.mask.template bit_pos<field_t>::pos << std::endl;
}


Currently I get an error

line 4: error: expected ‘;’ before ‘::’ token