'C++: bit-fields not works correctley with 1-bit fields
I have write the code to save some data with structure like CIOParams_b_t (see declaration) to file and load them. Number fields (>1 bits) load from file successfully, however boolean fields (1 bit) load with mistakes, maybe rendomely.
save data procedure:
//...
std::ofstream file;
file.open(path, std::fstream::out|std::ios::binary|std::ofstream::trunc);
//...
union {
CIOParams_b_t params_b_t;
char raw[6];
} p_data;
p_data.params_b_t.scr_width = params.scr_width;
p_data.params_b_t.scr_hight = params.scr_hight;
p_data.params_b_t.bits = static_cast<int>(params.bits);
p_data.params_b_t.brightness = params.brightness;
p_data.params_b_t.contrast = params.contrast;
p_data.params_b_t.fullscr = getFlag(params.flags, FLAG_FULLSCREEN)?1:0;
p_data.params_b_t.vsync = getFlag(params.flags, FLAG_VSYNC)?1:0;
p_data.params_b_t.mipmap = getFlag(params.flags, FLAG_MIPMAP)?1:0;
p_data.params_b_t.skybox = getFlag(params.flags, FLAG_SKYBOX)?1:0;
file.write(p_data.raw, 6);
//...
load data procedure:
//...
std::ifstream file(path);
//...
union {
CIOParams_b_t params_b_t;
char raw[6];
} p_data;
file.read(p_data.raw, 6);
retVal.scr_width = p_data.params_b_t.scr_width;
retVal.scr_hight = p_data.params_b_t.scr_hight;
retVal.bits = static_cast<CIOParams::cbits>(p_data.params_b_t.bits);
retVal.brightness = p_data.params_b_t.brightness;
retVal.contrast = p_data.params_b_t.contrast;
setFlag(&retVal.flags, FLAG_FULLSCREEN, p_data.params_b_t.fullscr==1?true:false);
setFlag(&retVal.flags, FLAG_VSYNC, p_data.params_b_t.vsync==1?true:false);
setFlag(&retVal.flags, FLAG_MIPMAP, p_data.params_b_t.mipmap==1?true:false);
setFlag(&retVal.flags, FLAG_SKYBOX, p_data.params_b_t.skybox==1?true:false);
file.close();
//...
structs declaration:
struct CIOParams
{
unsigned short int scr_width;
unsigned short int scr_hight;
unsigned char brightness;
unsigned char contrast;
enum cbits
{
b8 = 0,
b16,
b32,
b64
} bits;
unsigned char flags;
bool hasError = false;
};
struct CIOParams_b_t
{
unsigned scr_width : 10;
unsigned scr_hight : 10;
unsigned bits : 2;
unsigned fullscr : 1;
unsigned vsync : 1;
unsigned brightness : 8;
unsigned contrast : 8;
unsigned mipmap : 1;
unsigned skybox : 1;
unsigned __unused : 6;
};
Notice: functions setFlag and getFlag are working successfully. Anyway, they performed well in unit tests.
Solution 1:[1]
The problem comes with padding over byte boundaries. So, you think that your "CIOParams_b_t" type is 6 bytes long. But that is not guaranteed. In my environment it is padded by the compiler to 8 bytes length:
#include <iostream>
struct CIOParams_b_t
{
unsigned scr_width : 10;
unsigned scr_hight : 10;
unsigned bits : 2;
unsigned fullscr : 1;
unsigned vsync : 1;
unsigned brightness : 8;
unsigned contrast : 8;
unsigned mipmap : 1;
unsigned skybox : 1;
unsigned __unused : 6;
};
int main() {
CIOParams_b_t c;
std::cout << sizeof(c) << '\n';
}
Then, of course, your function cannot work.
You may have the idea to try with 8 bytes now, and it might work. But this is not the correct solution.
What you need is so called serialization. There are many ready to use libraries available for free. But for your few data, you can also simply overwrite the inserter << and extraction >> operator for your class.
Short example:
#include <iostream>
#include <string>
#include <fstream>
struct CIOParams_b_t
{
unsigned scr_width : 10;
unsigned scr_hight : 10;
unsigned bits : 2;
unsigned fullscr : 1;
unsigned vsync : 1;
unsigned brightness : 8;
unsigned contrast : 8;
unsigned mipmap : 1;
unsigned skybox : 1;
unsigned __unused : 6;
friend std::istream& operator >> (std::istream& is, CIOParams_b_t& c) {
unsigned tmp;
is >> tmp; c.scr_width = tmp;
is >> tmp; c.scr_hight = tmp;
is >> tmp; c.bits = tmp;
is >> tmp; c.fullscr = tmp;
is >> tmp; c.vsync = tmp;
is >> tmp; c.brightness = tmp;
is >> tmp; c.contrast = tmp;
is >> tmp; c.mipmap = tmp;
is >> tmp; c.skybox = tmp;
return is;
}
friend std::ostream& operator << (std::ostream& os, const CIOParams_b_t& c) {
return os << c.scr_width << '\n' << c.scr_hight << '\n' << c.bits << '\n' << c.fullscr << '\n' << c.vsync << '\n'
<< c.brightness << '\n' << c.contrast << '\n' << c.mipmap << '\n' << c.skybox << '\n';
}
};
const std::string fileName{ "tmp.txt" };
int main() {
CIOParams_b_t c1{1,3,3,0,1,4,5,0,1,0};
if (std::ofstream outFileStream{ fileName }; outFileStream)
outFileStream << c1;
else
std::cerr << "\nError: Could not open '" << fileName << "' for writing\n";
if (std::ifstream inFileStream{ fileName }; inFileStream) {
CIOParams_b_t c2{};
inFileStream >> c2;
std::cout << c2;
}
else
std::cerr << "\nError: Could not open '" << fileName << "' for reading\n";
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Armin Montigny |

