admin管理员组文章数量:1332345
I have a complicated struct with sub-structs to store the settings of my program. This structs contain some std::vectors
. I save the struct with this easy function:
void saveSettings(std::string filename)
{
std::ofstream file(filename, std::ios::binary);
if (file.is_open())
{
file.write(reinterpret_cast<const char*>(&Settings), sizeof(Settings));
file.close();
}
else
std::cout << "Error saving Settings.dat" << std::endl;
}
When the program loads, it reads the settings-file and stores the data in my settings-struct again. That would work fine if not for the vectors. What causes problems is that the state of the vectors is restored, including the data-pointer, but the data-array itself does no longer exist. As soon as I do a resize or something else that changes the array-size, the vector tries to delete the old data-array which of course fails (and of course I cannot read/write to any elements).
I neither need nor want that data in my settings-file. I could find another way to store the settings-struct (without the vectors), but I think that would be much more complicated and would make other things problematic. Is there a way to "reset" a vector or maybe give it a valid data-array?
One thing that actually does work is this:
struct VectorHack
{
size_t test1;
size_t test2;
size_t test3;
size_t test4;
};
VectorHack& hackedVec = reinterpret_cast<VectorHack&>(vec);
//hackedVec.test1 = 0;
hackedVec.test2 = 0;
hackedVec.test3 = 0;
hackedVec.test4 = 0;
This seems to "reset" the vector. It doesn't seem to make a difference whether or not I set test1
to 0, but I have no idea, what this number is for (it is different, every time a new vector is created).
Of course this is a very dirty hack, that could cause problems and that probably will not be portable. Can anyone point me to a better and easy solution to either "reset" the vectors or store the structs without them? I tried emptying the vectors before they are stored (and that seems to set the data-pointer to nullptr
), but the program still crashes as soon as I do a resize.
I have a complicated struct with sub-structs to store the settings of my program. This structs contain some std::vectors
. I save the struct with this easy function:
void saveSettings(std::string filename)
{
std::ofstream file(filename, std::ios::binary);
if (file.is_open())
{
file.write(reinterpret_cast<const char*>(&Settings), sizeof(Settings));
file.close();
}
else
std::cout << "Error saving Settings.dat" << std::endl;
}
When the program loads, it reads the settings-file and stores the data in my settings-struct again. That would work fine if not for the vectors. What causes problems is that the state of the vectors is restored, including the data-pointer, but the data-array itself does no longer exist. As soon as I do a resize or something else that changes the array-size, the vector tries to delete the old data-array which of course fails (and of course I cannot read/write to any elements).
I neither need nor want that data in my settings-file. I could find another way to store the settings-struct (without the vectors), but I think that would be much more complicated and would make other things problematic. Is there a way to "reset" a vector or maybe give it a valid data-array?
One thing that actually does work is this:
struct VectorHack
{
size_t test1;
size_t test2;
size_t test3;
size_t test4;
};
VectorHack& hackedVec = reinterpret_cast<VectorHack&>(vec);
//hackedVec.test1 = 0;
hackedVec.test2 = 0;
hackedVec.test3 = 0;
hackedVec.test4 = 0;
This seems to "reset" the vector. It doesn't seem to make a difference whether or not I set test1
to 0, but I have no idea, what this number is for (it is different, every time a new vector is created).
Of course this is a very dirty hack, that could cause problems and that probably will not be portable. Can anyone point me to a better and easy solution to either "reset" the vectors or store the structs without them? I tried emptying the vectors before they are stored (and that seems to set the data-pointer to nullptr
), but the program still crashes as soon as I do a resize.
- Comments have been moved to chat; please do not continue the discussion here. Before posting a comment below this one, please review the purposes of comments. Comments that do not request clarification or suggest improvements usually belong as an answer, on Meta Stack Overflow, or in Stack Overflow Chat. Comments continuing discussion may be removed. – blackgreen ♦ Commented Nov 21, 2024 at 8:24
2 Answers
Reset to default 0For serialize-unserialize binary. we need to deal with some problem.
- memory alignment link.
- endianness link.
- I/O error especially when read file.
Assume your program run on Linux and Windows, we can cut endianness problem.
If you want to keep your dirty method your must change std::vector
to fixed size (max size of your sub setting) array and add size of sub setting to setting then your dirty method should work.
If your want better method to save binary data to file but don't want to use general purpose format you need to design the structure data in file that easy to read correctly
.
I can show you some example.
#include <inttypes.h>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <vector>
struct SubSetting {
int32_t a;
uint64_t b;
};
struct Setting {
int32_t x;
uint64_t y;
std::vector<SubSetting> subs;
};
void serialize(std::ostream& os, const SubSetting& sub) {
// os.write(reinterpret_cast<const char*>(&sub), sizeof(SubSetting)); don't do this or we might write memory alignment
os.write(reinterpret_cast<const char*>(&sub.a), sizeof(int32_t));
os.write(reinterpret_cast<const char*>(&sub.b), sizeof(uint64_t));
/**
* SubSetting data | a = 4bytes | b = 8bytes | = 12bytes
*/
}
void unserialize(std::istream& is, SubSetting& sub) {
is.read(reinterpret_cast<char*>(&sub.a), sizeof(int32_t))
.read(reinterpret_cast<char*>(&sub.b), sizeof(uint64_t));
}
void serialize(std::ostream& os, const std::vector<SubSetting>& subs) {
uint64_t size = subs.size(); // ensure my size is 8 bytes
os.write(reinterpret_cast<char*>(&size), sizeof(uint64_t)); // it's much easier if we know size of element before read.
for (auto& sub : subs) {
serialize(os, sub);
}
/**
* std::vector<SubSetting> data | size of element = 8 bytes | SubSetting data 1 | SubSetting data 2 | SubSetting data ...
*/
}
void unserialize(std::istream& is, std::vector<SubSetting>& subs) {
uint64_t size;
is.read(reinterpret_cast<char*>(&size), sizeof(uint64_t));
subs.reserve(size); // prevert reallocate memory
for (uint64_t i = 0; i < size; i++) {
unserialize(is, subs.emplace_back()); // emplace_back() return ref in > c++17 only
}
}
void serialize(std::ostream& os, const Setting& setting) {
os.write(reinterpret_cast<const char*>(&setting.x), sizeof(int32_t));
os.write(reinterpret_cast<const char*>(&setting.y), sizeof(uint64_t));
serialize(os, setting.subs);
/**
* Setting data | x = 4bytes | y = 8bytes | std::vector<SubSetting> data |
*/
}
void unserialize(std::istream& is, Setting& setting) {
is.read(reinterpret_cast<char*>(&setting.x), sizeof(int32_t))
.read(reinterpret_cast<char*>(&setting.y), sizeof(uint64_t));
unserialize(is, setting.subs);
}
void print_setting(const Setting& setting) {
std::cout << "setting: x=" << setting.x << " y=" << setting.y << "\n"
<< "sub setting\n";
for (auto& sub : setting.subs) {
std::cout << "a=" << sub.a << " b=" << sub.b << "\n";
}
}
void save_setting(const std::filesystem::path& path, const Setting& setting){
std::ofstream ofs(path, std::ios::binary);
serialize(ofs, setting);
}
int load_setting(const std::filesystem::path& path, Setting& to){
std::ifstream ifs(path, std::ios::binary);
unserialize(ifs, to);
return 0;//error code 0 = no error
}
int main() {
auto temp_file = std::filesystem::temp_directory_path();
temp_file /= "setting.temp";
Setting setting{};
setting.x = 7;
setting.y = 8;
setting.subs.emplace_back(SubSetting{11, 22});
setting.subs.emplace_back(SubSetting{33, 44});
setting.subs.emplace_back(SubSetting{55, 66});
save_setting(temp_file, setting);
Setting loaded_setting{};
auto ec = load_setting(temp_file, loaded_setting);
if(ec){
std::cerr << "load setting fail.\n";
return 0;
}
print_setting(loaded_setting);
}
godbolt
Explanation is in program comment. To make serialize working properly. Data size is very important.
Thanks to some commentators, I got some ideas that solve my problem, so here is my answer to my question. Like I described in my question, the data in the vectors is not important and should not be stored. The main reason for the vectors to be in my settings-struct is to have the general structure (so to be able to access the vectors via the struct).
Idea #1: Simply don't store vectors in my struct, but pointers to these vectors. When (binary) loading my struct, those pointers are invalid, but I can simply create the vectors in the load-function and set the pointers accordingly. The only difference in the code when using the vectors will be replacing .
with ->
.
Idea #2 (a bit more complicated): Create my own vector-class with a method "reset" that simply creates an array and set the data-pointer to that without deleting the old (non-existent) array.
Idea #3 (probably most general and portable, but less efficient): Don't use a struct, but a Map with for example an std::string
for the key. That way, I can iterate over the "fields" of the map and serialize automatically, without having to manually put every change off my "struct" in the load/save functions.
本文标签: cStoring a struct that contains stdvector without the vectordataStack Overflow
版权声明:本文标题:c++ - Storing a struct that contains std::vector without the vector-data - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742327304a2453981.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论