c++ - std::ifstream setting fail() even when no error -
using gcc 4.7.3 on cygwin 1.7.24. compiler options include: -std=gnu++11 -wall -wextra
i working on command line application , needed able load , save set of strings wrote quick wrapper class around std::set add load , save methods.
// keyset.h #ifndef keyset_h #define keyset_h #include <cstdlib> #include <sys/stat.h> #include <cerrno> #include <cstring> #include <string> #include <set> #include <iostream> #include <fstream> inline bool file_exists (const std::string& filename) { /* utility routine check existance of file. returns true or false, prints error , exits status 2 on error. */ struct stat buffer; int error = stat(filename.c_str(), &buffer); if (error == 0) return true; if (errno == enoent) return false; std::cerr << "error while checking '" << filename << "': " << strerror(errno) << std::endl; exit (2); } class keyset { private: std::string filename; std::set<std::string> keys; public: keyset() {} keyset(const std::string pfilename) : filename(pfilename) {} void set_filename (const std::string pfilename) {filename = pfilename;} std::string get_filename () {return filename;} auto size () -> decltype(keys.size()) {return keys.size();} auto cbegin() -> decltype(keys.cbegin()) {return keys.cbegin();} auto cend() -> decltype(keys.cend()) {return keys.cend();} auto insert(const std::string key) -> decltype(keys.insert(key)) {return keys.insert(key);} void load (); void save (); }; void keyset::load () { if (file_exists(filename)) { errno = 0; std::ifstream in (filename, std::ios_base::in); if (in.fail()) { std::cerr << "error opening '" << filename << "' reading: " << strerror(errno) << std::endl; exit (2); } std::string token; if (token.capacity() < 32) token.reserve(32); while (in >> token) keys.insert(token); if (!in.eof()) { std::cerr << "error reading '" << filename << "': " << strerror(errno) << std::endl; exit (2); } in.clear(); // need clear flags before calling close in.close(); if (in.fail()) { std::cerr << "error closing '" << filename << "': " << strerror(errno) << std::endl; exit (2); } } } void keyset::save () { errno = 0; std::ofstream out (filename, std::ios_base::out); if (out.fail()) { std::cerr << "error opening '" << filename << "' writing: " << strerror(errno) << std::endl; exit (2); } (auto key = keys.cbegin(), end = keys.cend(); key != end; ++key) { out << *key << std::endl; } out.close(); if (out.fail()) { std::cerr << "error writing '" << filename << "': " << strerror(errno) << std::endl; exit (2); } } #endif //
here's quick program test load method.
// ks_test.cpp #include "keyset.h" int main() { keyset test; std::string filename = "foo.keys.txt"; test.set_filename(filename); test.load(); (auto key = test.cbegin(), end = test.cend(); key != end; ++key) { std::cout << *key << std::endl; } }
the data file has "one 2 three" in it.
when go run test program, following error test program:
$ ./ks_test error closing 'foo.keys.txt': no error
both cppreference.com , cplusplus.com close method should set fail bit on error. save method works fine, , load method works correctly if comment out error check after close. should work or have misunderstood how close supposed work? in advance.
edited clarify, fix typo's , adjust code per joachim pileborg's , konrad rudolph's comments.
edited add solution code.
you have 2 errors here: first how reading, more loop reading. eof
flag not set until after tried read , read failed. instead should this:
while (in >> token) { ... }
otherwise loop 1 time many , try read beyond end of file.
the second problem 1 notice, , depends on the first problem. since try read beyond end of file, stream set failbit
causing in.fail()
return true
though there no real error.
Comments
Post a Comment