I have previously railed about the lack of support for bit fields in embedded libraries. It turns out there is a good reason for this: they are not portable, and very ill defined in the standard.
In order to provide reasonably portable implementations, we should provide a well defined alternative to bit fields, which capture the essence of what we are now doing with bit manipulation.
- Each bit field must be based on a basic integer type. This makes the mapping to storage explicit, and reduces the chance for counting errors.
- You can explicitly choose whether the fields start with the highest or lowest bit
- You can specify whether the access to the field must be atomic or not
- You can define a force value for each field. This value is written to a
volatile
bit field whenever you update any field, unless you specifically have changed the value in the update statement, and it also serves as the default during initialization. - We force the compiler to collect consecutive write statements to a field, and update the field with exactly one write, when you use the comma operator to separate the statements.
We could define define bit fields as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
bitfield< little_endian, unsigned int, atomic> ClearFlags { int clear_a : 1 = 0; int : 7 = 0; int clear_b : 1 = 0; int : = 0; }; volatile ClearFlags* pClear; bitfield< big_endian, unsigned char, atomic> ChangeSettings { bool flag_1 : 1; bool clear_2 : 1 = false; int : 4 = 0; int : 2; }; volatile ChangeSettings* pChangeSettings; |
Then we can clear either one or both of flags easily:
1 2 3 4 5 6 7 |
// clear one flag pClear->clear_a = 1; // and the other pClear->clear_b = 1; // or both together pClear->clear_b = 1, pClear->clear_a = 1; |
Things are a bit more complicated when we mix stuff:
1 2 3 4 5 6 |
// read the data, set flag_1 = true, clear_2 = false, int:4 = 0, and write it back pChangeSettings->flag_1 = true; // this will just update flag_1 ChangeSettings val = *pChangeSettings; val.flag_1 = true; *pChangeSettings = val; |
And I wonder whether we should add new operators ->{
and .{
. This would allow us to write pClear->{clear_a = 1, clear_b = 1};
to simplify updating multiple fields in one go.