Search code examples
c++vectorsaveifstreambitset

How to load/read binary file into an "vector<vector< bitset<32> >" in c++?


I have a problem with loading/reading an

vector<vector< bitset<32> > 

(called/typedef'ed as population in my code)

which it stored using the following code:

void genetic_algorithm::save_generation(population pop_to_save, string filename){
  std::ofstream file(filename, std::ofstream::binary);
  unsigned long n;
  for(size_t i = 0; i < pop_to_save.size(); i++ )
  {
    if ( pop_to_save[i].size() > 0 )
    {
      n = pop_to_save[i][0].to_ulong();
      const char* buffer = reinterpret_cast<const char*>(&n);
      file.write(buffer, pop_to_save[i].size());
    }
  }
}

The thing i need is therefore a function that can load, i.e.:

population genetic_algorithm::load_generation(string filename){
  // TODO
}

Best Regards,

Mathias.

EDIT

I have solved the problem on my own (with a bit of help from the comment) Here is the final code for any that might face the same problem:

void genetic_algorithm::save_generation(population pop_to_save, string filename){
  std::ofstream file(filename, std::ofstream::binary);
  unsigned long n;
  for(size_t i = 0; i < pop_to_save.size(); i++ ) {
    for (size_t j = 0; j < pop_to_save[i].size(); j++) {
      n = pop_to_save[i][j].to_ulong();
      file.write(reinterpret_cast<const char*>(&n), sizeof(n));
    }
  }

  std::cout << "Saved population to: " << filename << '\n';
}

population genetic_algorithm::load_generation(string filename){
  std::ifstream file(filename, std::ofstream::binary);
  population loaded_pop (20, std::vector<bitset<32>> (394,0));
  unsigned long n;
  for(size_t i = 0; i < 20; i++ ) {
    for (size_t j = 0; j < 394; j++) {
      file.read( reinterpret_cast<char*>(&n), sizeof(n) );
      loaded_pop[i][j] = n;
    }
  }

  std::cout << "Loaded population from: " << filename << '\n';
  return loaded_pop;
}

Solution

  • I examined your method genetic_algorithm::save_generation() and this what I found out:

    • bitset<32> stores 32 bits in packed form (probably an unsigned int with 32 bits or something similar).

    • vector<bitset<32> > stores a dynamic array of such bit sets.

    • vector<vector<bitset<32> > > stores a dynamic array of such dynamic arrays of bit sets.

    So far, so good. In your method genetic_algorithm::save_generation(), you loop through all elements of the outer vector to dump the contents of each inner vector (if not empty).

    The file.write(buffer, pop_to_save[i].size()); does probably not what you want. The 2nd arg. of std::ostream::write() is the number of characters. You pass the size() of the resp. inner vector which returns the number of elements (but not the size in bytes). But the element size (bitset<32>) is probably bigger than a character. A better approach (though not the best) would be to use instead file.write(buffer, 4 * pop_to_save[i].size());. (sizeof (char): 1, sizeof bitset<32>: hopefully 4 but I would check this.)

    Thus, more safe would be to use file.write(buffer, pop_to_save[i].size() * sizeof pop_to_save[i][0]);. This is better but still not perfect. If you want to use your application on multiple platforms this may fail because the file format becomes platform dependent now. (It depends on sizeof pop_to_save[i][0].)

    Thus, the best approach would be to make a nested loop for the inner vectors also and to store the elements of them individually with a type of platform independent size (e.g. std::uint32_t).

    However, my actual concern is a design issue:

    If you store a dynamic array of dynamic arrays (where each inner array might be of arbitrary size) then you have to store the number of elements for these inner arrays also. This is what I miss in your method genetic_algorithm::save_generation().

    May be, your problem of loading is caused by the fact that you have to reserve or resize the inner vectors for the bits to load but you do not know with which size. My answer: You cannot know. This info is missing (and had to be provided somehow, e.g. by the file itself).