1

I pretend to store a std::vector of std::complex<float> in a binary file, and to later load that data into a std::vector again. I have checked similar questions in SO and came up with the following code:

#include <complex>
#include <iostream>
#include <memory>
#include <fstream>
#include <iterator>
#include <random>
#include <vector>

using namespace std;
typedef vector<complex<float>> complex_vector;


float get_random(){
    static std::default_random_engine e;
    static std::uniform_real_distribution<> dis(-10, 10);
    return dis(e);
}

int main(){
    // Random number generator
    srand(static_cast<unsigned> (time(0)));

    // Create a vector with 25 random complex<float> values
    std::shared_ptr<vector<complex<float>>> buffer = std::make_shared<vector<complex<float>>>();
    for(unsigned int i=0; i<25; i++){
        buffer->push_back(complex<float>(get_random(), get_random()));
    }

    // Write those values into a binary file
    std::string binFileName = "aux.bin";
    std::ofstream rawFile(binFileName, std::ios::binary);
    for(unsigned int i=0; i<buffer->size(); i++){
        rawFile.write(reinterpret_cast<const char*>(&buffer->at(i)), sizeof(complex<float>));
    }
    rawFile.close();

    // Load the binary file into the buffer
    std::ifstream input(binFileName, std::ios::binary);
    complex_vector auxBuffer{std::istream_iterator<complex<float>>(input), std::istream_iterator<complex<float>>()};
    unsigned int samplesRead = auxBuffer.size();
    std::cout << samplesRead;

    return 0;
}

Output:

0

What am I missing?

EDIT: After NathanOliver's answer, this is how my code looks like now:

#include <complex>
#include <iostream>
#include <memory>
#include <fstream>
#include <iterator>
#include <random>
#include <vector>

using namespace std;
typedef vector<complex<float>> complex_vector;


float get_random(){
    static std::default_random_engine e;
    static std::uniform_real_distribution<> dis(-10, 10);
    return dis(e);
}

int main(){
    // Random number generator
    srand(static_cast<unsigned> (time(0)));

    // Create a vector with 25 random complex<float> values
    std::shared_ptr<complex_vector> buffer = std::make_shared<complex_vector>();
    for(unsigned int i=0; i<25; i++){
        buffer->push_back(complex<float>(get_random(), get_random()));
    }

    // Write those values into a binary file
    std::string binFileName = "aux.bin";
    std::ofstream rawFile(binFileName, std::ios::binary);
    rawFile.write(reinterpret_cast<const char*>(buffer->size()), sizeof(buffer->size()));
    rawFile.write(reinterpret_cast<const char*>(buffer->data()), sizeof(std::complex<float>) * buffer->size());
    rawFile.close();

    // Load the binary file into the buffer
    std::ifstream input(binFileName, std::ios::binary);
    complex_vector::size_type nSamples;
    input.read(reinterpret_cast<char*>(nSamples), sizeof(complex_vector::size_type));
    std::cout << nSamples;
    complex_vector *destination = new complex_vector(25);
    input.read(reinterpret_cast<char*>(destination->data()), sizeof(std::complex<float>) * nSamples);

    return 0;
}

I'm getting a SIGSEGV in the first call to write function.

Also, I don't understand why I write , but I have to read .

Roman Rdgz
  • 11,378
  • 36
  • 110
  • 192
  • You are probably missing the `std::ios::out` and `std::ios::in` flags. Did you check if your file was actually created and contains data? – paddy Feb 13 '17 at 11:55
  • 2
    You can't mix binary writes with formatted reads. If you `write` the data you need to `read` the data. To make like easier you should `write` the size of the vector first that way you can `read` it in and allocate the storage so you can `read` in the data. – NathanOliver Feb 13 '17 at 12:35
  • And dont mix `srand` with `std::default_random_engine` :) – A.S.H Feb 13 '17 at 12:45
  • @NathanOliver where's exactly the problem? I saw similar examples done just like this... I am both writing and reading in binary, so I don't understand what's wrong – Roman Rdgz Feb 13 '17 at 12:55
  • Your not reading in binary. `complex_vector auxBuffer{std::istream_iterator>(input), std::istream_iterator>()};` does not do a binary read even if the file is open in binary mode. – NathanOliver Feb 13 '17 at 12:55

1 Answers1

1

You can't mix binary writes with formatted reads which is what you are doing right now. Even though the file is opened in binary mode

complex_vector auxBuffer{std::istream_iterator<complex<float>>(input), std::istream_iterator<complex<float>>()};

calls the operator >>ofcomplex>and notreadwhich is what you should use if you write the data withwrite`.

You have two ways you can fix this. You can do formatted writes instead of binary like

for (const auto& e : *buffer)
    rawFile << e << "\n"; // or space or any other white space delimiter

And then you would read it in exactly as you have it.

Your other option is to write the size of the vector and the contents of the vector and then read those both back in like

// write the size of the vector
rawFile.write(reinterpret_cast<const char*>(&buffer->size()), sizeof(buffer->size()));
// write the whole contents of the vector in one go
rawFile.write(reinterpret_cast<const char*>(buffer->data()), sizeof(std::complex<float>) * buffer->size());

// and then to read it back in we get the size
complex_vector::size_type size;
input.read(reinterpret_cast<char*>(&size), sizeof(size));
// construct a vector of that size
complex_vector auxBuffer(size);
// and then read the data back into the vector
input.read(reinterpret_cast<char*>(auxBuffer.data()), sizeof(std::complex<float>) * auxBuffer->size());

Personally I like the first option as it looks a lot cleaner but if this is performance sensitive then binary mode is typically faster.

NathanOliver
  • 150,499
  • 26
  • 240
  • 331
  • I need it to be binary due to the size of the output files, which gets too heavy. Second option does not compile: invalid conversion from ‘const char*’ to ‘std::basic_istream::char_type* {aka char*}’ [-fpermissive] (on the first read call). Also, I don't understand why it doesn't work as I tried, just as specified here: http://stackoverflow.com/a/5420568/808091 – Roman Rdgz Feb 13 '17 at 13:47
  • @RomanRdgz I had an error in the code. The calls to write needed to use `reinterpret_cast` and not `reinterpret_cast`. The code has been updated. – NathanOliver Feb 13 '17 at 13:52
  • Still does not seem to work: http://cpp.sh/7fhuz Plus, in my local code, I am getting a SIGSEGV at the first write – Roman Rdgz Feb 13 '17 at 14:16
  • @RomanRdgz It is not going to work on C++ Shell as you cannot create files. I did find another bug and `sizeof(std::complex) * buffer->size()` should be `sizeof(std::complex) * auxBuffer->size()`. So `rawFile.write(reinterpret_cast(buffer->size()), sizeof(buffer->size()));` gives you a sigsev? – NathanOliver Feb 13 '17 at 14:20
  • That's right, I'm getting a SIGSEGV right there. Previously I'm declaring std::ofstream rawFile("aux.bin", std::ios::binary); and the file does get created. Next line is the write one with the sigsegv and don't know why – Roman Rdgz Feb 13 '17 at 14:35
  • @RomanRdgz Your not writing it inside a for loop anymore are you? I'm going to set this up on my machine latter today and see what happens. I could have missed something. – NathanOliver Feb 13 '17 at 14:38
  • No, not anymore. I'll edit the question with my current code – Roman Rdgz Feb 13 '17 at 14:44
  • Missing '&' in rawFile.write(reinterpret_cast(&buffer->size()), sizeof(buffer->size())); was the problem. Now working! – Roman Rdgz Feb 14 '17 at 08:32
  • @RomanRdgz Awesome. Sorry about that, Forgot I was dealing with a pointer. – NathanOliver Feb 14 '17 at 12:34