1

I am encountering a situation that I don't quite know where to look after the cause of the issue for, as the problem seems to only occur when piping the program output from the terminal into a file and I have no idea what that might have to do with my code itself. Furthermore this only happens for larger files and not for small ones;

I wrote a file reader class with an output iterator:

#include <fstream>
#include <iostream>
#include <limits>

class Filereader
{
private:
    std::fstream file;

    class output_iterator
    {

    public:
        output_iterator(std::fstream& file, int line_number)
          : file(file), line_number(line_number)
        {
            if (line_number == 0 || line_number == -1) {
            } else {
                go_to_line(line_number);
            }
        }

        const std::string& operator*() const { return line; }

        output_iterator& operator++()
        {
            if (file.good()) {
                getline(file, line);
                line_number++;
            } else {
                line_number = -1;
            }
            return *this;
        }

        friend bool operator!=(const output_iterator& lhs,
                               const output_iterator& rhs)
        {
            return !(lhs == rhs);
        }

        friend bool operator==(const output_iterator& lhs,
                               const output_iterator& rhs)
        {
            if (&lhs.file != &rhs.file)
                throw(std::invalid_argument("file operands do not match"));
            return lhs.line_number == rhs.line_number;
        }

    private:
        void go_to_line(size_t num)
        {
            file.seekg(0, std::ios::beg);
            for (size_t i = 0; i < num - 1; ++i) {
                file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
            }
        }

    private:
        std::fstream& file;
        std::string   line;
        int           line_number;
    };

public:
    Filereader(const std::string& filename)
    {
        file.exceptions(std::fstream::failbit | std::fstream::badbit);
        try {
            file.open(filename, std::ios::in);
        } catch (const std::fstream::failure& e) {
            std::cerr << "exception opening file '" + filename + "'"
                      << "\n";
        }
    }

    output_iterator begin() { return output_iterator(file, 0); }
    output_iterator end() { return output_iterator(file, -1); }
};

and my test program is this:

int main(int argc, char** argv){

    Filereader fr(argv[1]);

    for (auto i = fr.begin(); i != fr.end(); ++i){
        std::cout << *i << "\n";
    }
}

Now, piping the output actually goes through successfully, but after that the following error is thrown:

terminate called after throwing an instance of 'std::ios_base::failure[abi:cxx11]'
  what():  basic_ios::clear: iostream error

And as mentioned this only happens for larger files.

When not piping the output, no error is being thrown... I am probably missing something really obvious here, but I don't know where to look for the cause of this.

lo tolmencre
  • 3,275
  • 17
  • 47
  • When you say that you're *piping* what do you mean by that? Can you please include how you use the program? – Some programmer dude Feb 20 '17 at 16:58
  • @Someprogrammerdude I use it like this `./program file_to_read_from > file_to_pipe_into` – lo tolmencre Feb 20 '17 at 17:00
  • That's not really *piping*, but *redirecting*. :) Anyway, have you tried to catch the exception using a *debugger*? It should stop at the location of the exception being thrown, helping you locate where in the code it is, and help you understand what the problem might be. – Some programmer dude Feb 20 '17 at 17:02
  • Also, you have a logical error in your iterator: The `begin` iterator should be dereferencable and the dereference operator should return the value of the first "element" (first line in your case). Right now the first line will be read after you *increase* the iterator. Should not matter for the problem you're having though. – Some programmer dude Feb 20 '17 at 17:03
  • Oh, and when you say "larger file" what do you mean by that? A few hundred KB? Some MB? GB? – Some programmer dude Feb 20 '17 at 17:05
  • @Someprogrammerdude 8,5 MB in this case – lo tolmencre Feb 20 '17 at 17:06
  • There's also *another* logical error in your iterator, which is related to [Why is iostream::eof inside a loop condition considered wrong?](http://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong). You check the status of the file *before* attempting to read the next line, but the `eofbit` is not set until *after* you try to read from beyond the end of the file. Use e.g. `if (std::getline(...)) { /* okay */ } else { /* failure */ }` instead. – Some programmer dude Feb 20 '17 at 17:10

0 Answers0