Should I use bit fields?

Pages: 123
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 


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

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

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 :(

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;

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?
Aaargh - that is exactly it! I'm a dumbass! :(
That's annoying that asio would define those symbols in the global namespace.
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) };
    };
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
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.]
Thanks once again guru! :)
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
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
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!
I'm wondering if Loki's GenScatterHierarchy or something like that would make more sense for what I'm trying to do?
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.
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 struct bit_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
Last edited on
Pages: 123