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.

Share Improve this question asked Nov 20, 2024 at 21:25 Paul AnerPaul Aner 5032 silver badges11 bronze badges 1
  • 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
Add a comment  | 

2 Answers 2

Reset to default 0

For 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