 Parent directory
Parent directory
 Makefile
Makefile
 grades-malformed.txt
grades-malformed.txt
 grades.txt
grades.txt
 io1.cpp
io1.cpp
 io2.cpp
io2.cpp
 io3.cpp
io3.cpp
CC  = g++
CXX = g++
CFLAGS   = -g -Wall
CXXFLAGS = -g -Wall -std=c++17
executables = io1 io2 io3
.PHONY: default
default: $(executables)
io1: io1.o
io2: io2.o
io3: io3.o
.PHONY: clean
clean:
	rm -f *~ a.out core *.o $(executables)
.PHONY: all
all: clean default   "Edgar Allan Poe" , 88.8
"Mark Twain",,    66.6
  "Herman Melville",77.7   "Edgar Allan Poe" , 88.8
"Mark Twain",     66.6
  "Herman Melville",77.7#include <iostream>
#include <iomanip>
void print_io_states(std::ios& io) {
    std::cout << '\t';
    std::cout << " fail()==" << io.fail();
    std::cout << "  bad()==" << io.bad();
    std::cout << "  eof()==" << io.eof();
    std::cout << '\n';
}
int sum_ints(std::istream& is) {
    int i, sum = 0;
    while (is >> i) {
        sum += i;
        std::cout << "current sum: " << std::setw(4) << sum;
        print_io_states(is);
    }
    return sum;
}
int main() {
    int sum = sum_ints(std::cin);
    std::cout << "  final sum: " << std::setw(4) << sum;
    print_io_states(std::cin);
}
/*  Lecture outline:
1.  Code overview
    - I/O Stream Library class hierarchy
        - https://en.cppreference.com/w/cpp/io
        - std::ios, std::istream, std::cin
    - istream& istream::operator>>( int& value );
    - ios::operator bool() const;
        - same as !ios::fail()
    - I/O manipulator
        - "out << setw(n)" will invoke "out.width(n)"
2.  ios_base::iostate flags and ios accessors
    - https://en.cppreference.com/w/cpp/io/ios_base/iostate
    - ios_base::iostate
        - failbit: formatting or parsing error
        - badbit: irrecoverable I/O error
        - eofbit: end-of-file (EOF) is reached
    - ios accessor functions:
        - fail(): failbit or badbit are set - should've been fail_or_bad()...
        - bad(): badbit is set
        - eof(): eofbit is set
        - operator bool(): equivalent to !fail()
3.  Examples runs:
    $ echo "10 2 3 4" | ./io1
    current sum:   10	 fail()==0  bad()==0  eof()==0
    current sum:   12	 fail()==0  bad()==0  eof()==0
    current sum:   15	 fail()==0  bad()==0  eof()==0
    current sum:   19	 fail()==0  bad()==0  eof()==0
      final sum:   19	 fail()==1  bad()==0  eof()==1
        - Note that echo "10 2 3 4" adds '\n' at the end,
          so 4 was read without hitting EOF.
    $ echo "10 abc 3 4" | ./io1 
    current sum:   10	 fail()==0  bad()==0  eof()==0
      final sum:   10	 fail()==1  bad()==0  eof()==0
        - "abc" failed to parse.
    $ echo -n "10 2 3 4" | ./io1
    current sum:   10	 fail()==0  bad()==0  eof()==0
    current sum:   12	 fail()==0  bad()==0  eof()==0
    current sum:   15	 fail()==0  bad()==0  eof()==0
    current sum:   19	 fail()==0  bad()==0  eof()==1
      final sum:   19	 fail()==1  bad()==0  eof()==1
        - echo -n does not add '\n' at the end, 
          so when 4 was read, we hit EOF.
*/#include <iostream>
#include <iomanip>
void print_io_states(std::ios& io) {
    std::cout << '\t';
    std::cout << " fail()==" << io.fail();
    std::cout << "  bad()==" << io.bad();
    std::cout << "  eof()==" << io.eof();
    std::cout << '\n';
}
int sum_ints(std::istream& is) {
    int i, sum = 0;
    while (!is.eof()) {
        is >> i;
        if (is.bad()) {
            throw std::runtime_error{"bad istream"};
        } else if (is.fail()) {
            // fail(), but not bad()
            if (is.eof()) {
                // failbit && eofbit: this is normal (trailing whitespace)
                break;
            }
            // failbit && !eofbit: probably non-digit; just skip it
            is.clear();
            char c;
            is.get(c);
            std::cout << "  skipped: " << c << '\n';
        } else {
            // read i successfully;
            // we could have hit eof or not (depends on trailing whitespace)
            sum += i;
            std::cout << "current sum: " << std::setw(4) << sum;
            print_io_states(is);
        }
    }
    return sum;
}
int main() {
    int sum = sum_ints(std::cin);
    std::cout << "  final sum: " << std::setw(4) << sum;
    print_io_states(std::cin);
}
/*  Lecture outline:
1.  sum_ints() rewritten to recover from error
    $ echo "10 abc 3 4" | ./io2
    current sum:   10	 fail()==0  bad()==0  eof()==0
      skipped: a
      skipped: b
      skipped: c
    current sum:   13	 fail()==0  bad()==0  eof()==0
    current sum:   17	 fail()==0  bad()==0  eof()==0
      final sum:   17	 fail()==1  bad()==0  eof()==1
2.  sum_ints() code walk-through
    - careful coverage of every possible case
    - ios::clear(), istream::get()
3.  C++ I/O streams vs. C standard I/O
    By default, std::cin, std::cout, and std::err are synchronized with stdin,
    stdout, and stderr.  This means that the C++ streams are unbuffered, and
    each I/O operation on a C++ stream is immediately applied to the
    corresponding C stream's buffer. This makes it possible to freely mix C++
    and C I/O.
    You can turn off this behavior by detaching C++ streams from C streams:
        std::ios_base::sync_with_stdio(false);
    Possible benefits of detaching C++ streams:
        - The C++ streams are allowed to buffer their I/O independently,
          which may increase performance.
    But you lose the following benefits when you detach C++ streams:
        - You can no longer mix C & C++ I/O.
        - C++ streams will no longer be thread-safe.
        - '\n' may not flush standard output connected to terminal.
    Recommendation:
        - Keep the default behavior (i.e. C++ & C I/O synchronized) unless
          performance measurement reveals significant overhead.
        - Use '\n' instead of std::endl for terminal output.
*/#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
using namespace std;
static ostream& operator<<(ostream& os, const pair<string,double>& e) {
    return os << "[" << e.first << "] (" << e.second << ")";
}
static istream& operator>>(istream& is, pair<string,double>& e) {
    char c;
    if (is >> c && c == '"') { // read opening quote, skipping whitespace
        string student;
        while (is.get(c) && c != '"') { // read all chars till closing quote
            student += c;                
        }
        if (c == '"') { // proceed if we read the name properly
            if (is >> c && c == ',') { // read a comma, skipping whitespace
                double grade;
                if (is >> grade) {
                    e = { student, grade };
                    return is;
                }
            }
        }
    }
    // if we are here, we could not read "student name", grade
    is.setstate(ios_base::failbit);
    return is;
}
int f1(istream& is);
int f2(istream& is);
int f3(const char *filename);
int main(int argc, char **argv) {
    try {
        return f1(cin);
        //return f2(cin);
        //return f3(argv[1]);
    } catch (const exception& x) {
        cerr << x.what() << '\n';
    }
}
int f1(istream& is) {
    pair<string,double> e;
    while (is >> e) {
        cout << e << '\n';
    }
    // istream::operator bool() returns false if fail() is true,
    // which means failbit == 1 or badbit == 1
    if (is.bad()) {
        throw runtime_error{"bad istream"};
    } else if (!is.eof()) {
        // failbit is set but we are not at the end, so parse error
        throw invalid_argument{"bad grade format"};
    } else {
        return 0;
    }
}
int f2(istream& is) {
    int r;
    string str;
    while (getline(is, str)) {
        istringstream iss(str);
        try {
            r = f1(iss);
        } catch (const invalid_argument& x) {
            cerr << x.what() << ": " << str << '\n';
        }
    }
    return r;
}
int f3(const char *filename) {
    ifstream ifs { filename };
    if (!ifs) {
        if (filename)
            throw runtime_error{"can't open file: "s + filename};
        else
            throw runtime_error{"no file name"};
    }
    return f2(ifs);
}
/*  Lecture outline:
1.  Code walk-through
    - Demo (f1 version)
    - std::pair class template
    - f1()
    - operator>>()
2.  String stream
    - Revisit I/O Stream Hierarchy: https://en.cppreference.com/w/cpp/io
    
    - f2():
        - getline() reads a line from an istream 
        - istringstream is an istream whose character source is a string
        - f2() calls f1() not with cin, but with an istringstream
        - Error recovery is achieved without modifying f1()
3.  File stream
    - f3():
        - ifstream is an istream whose character source is a file
        - f3() calls f2() not with cin, but with an ifstream
        - Reading from a file without redirection is achieved without 
          modifying f2()
    - Also note:
        - RAII paradigm of ifstream: the constructor opens the file
          and the destructor closes it
        - failbit is set if it fails to open the file
        - ""s denotes an std::string literal
*/