COMS W4995 C++ for C Programmers

Index of 2023-5/code/0606

Parent directory
entries-malformed.txt
entries.txt
io1.cpp
io2.cpp
io3.cpp
template4.cpp
to.cpp

entries-malformed.txt

   { "Barack Obama" , 44 }

{"Donald Duck",,   45}  

  { "Joe Biden",46 }

entries.txt

   { "Barack Obama" , 44 }

{"Donald Duck",   45}  

  { "Joe Biden",46 }

io1.cpp

#include <iostream>
#include <iomanip>

using namespace std;

void print_io_states(ios& io)
{
    cout << " fail()==" << io.fail();
    cout << "  bad()==" << io.bad();
    cout << "  eof()==" << io.eof();
}

int sum_ints(istream& is)
{
    int i, sum = 0;

    while (is >> i) {
        sum += i;
        cout << "current sum: " << setw(4) << sum;

        cout << '\t'; 
        print_io_states(is);
        cout << '\n';
    }
    return sum;
}

int main()
{
    int sum = sum_ints(cin);

    cout << "  final sum: " << setw(4) << sum;

    cout << '\t'; 
    print_io_states(cin);
    cout << '\n';
}

io2.cpp

#include <iostream>
#include <iomanip>

/* 
 * You can detach C++ I/O streams from C standard I/O by doing this:
 *
 *     std::ios_base::sync_with_stdio(false);
 *
 * When sync_with_stdio is set to true (default mode), C++ streams are
 * unbuffered, and each I/O operation on a C++ stream is immediately 
 * applied to the corresponding C stream's buffer. When it is set to 
 * false, the C++ streams are allowed to buffer their I/O independently,
 * which may be considerably faster in some cases.
 *
 * But note the following when it is set to false:
 *
 *   - 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
 */

using namespace std;

void print_io_states(ios& io)
{
    cout << " fail()==" << io.fail();
    cout << "  bad()==" << io.bad();
    cout << "  eof()==" << io.eof();
}

int sum_ints(istream& is)
{
    int i, sum = 0;

    while (!is.eof()) {
        is >> i;
        if (is.bad()) {
            throw 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);
            cout << "  skipped: " << c << '\n';
        } else {
            // read i successfully;
            // we could have hit eof or not (depends on trailing whitespace)
            
            sum += i;
            cout << "current sum: " << setw(4) << sum;

            cout << '\t';
            print_io_states(is);
            cout << '\n';
        }
    }
    return sum;
}

int main()
{
    int sum = sum_ints(cin);

    cout << "  final sum: " << setw(4) << sum;

    cout << '\t'; 
    print_io_states(cin);
    cout << '\n';
}

io3.cpp

#include <iostream>
#include <iomanip>
#include <fstream>

using namespace std;

struct Entry {
    string name;
    int number;
};

ostream& operator<<(ostream& os, const Entry& e)
{
    return os << "{\"" << e.name << "\", " << e.number << "}";
}

/*
 * Read { "name" , number } pair from an istream.
 *
 *   - code from "A Tour of C++", Sec 11.5 (3rd ed.) / Sec 10.5 (2nd ed.)
 */
istream& operator>>(istream& is, Entry& e)
{
    char c, c2;
    if (is>>c && c=='{' && is>>c2 && c2=='"') { // start with a { "
        string name;
        while (is.get(c) && c!='"') // anything before " is part of name
            name+=c;

        if (is>>c && c==',') {
            int number = 0;
            if (is>>number>>c && c=='}') { // read the number and a }
                e = {name,number}; // assign to the entry
                return is;
            }
        }
    }
    is.setstate(ios_base::failbit); // register the failure in the stream
    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)
{
    Entry e;
    while (is >> e) {
        cout << e << '\n';
    }

    // istream::operator bool() returns false if fail(), 
    // which means failbit==1 or  badbit==1

    if (is.bad()) {
        throw runtime_error{"bad istream"};
    } else if (!is.eof()) {
        throw invalid_argument{"bad entry 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);
}

template4.cpp

#include <string>
#include <vector>
#include <iostream>

template <typename T>
class Vec {
public:
    Vec() : sz{0}, cap{1}, a{new T[cap]} {}

    ~Vec() {
        delete[] a;
    }

    Vec(const Vec&) = delete;
    Vec& operator=(const Vec&) = delete;

    // Vec(const Vec&) = default;
    // Vec& operator=(const Vec&) = default;

    Vec(Vec&& ia) : sz{ia.sz}, cap{ia.cap}, a{ia.a} {
        ia.sz = ia.cap = 0;
        ia.a = nullptr;
        std::cout << "move ctor" << std::endl;
    }

    Vec& operator=(Vec&& ia) {
        if (this != &ia) {
            delete[] a;

            sz = ia.sz;
            cap = ia.cap;
            a = ia.a;

            ia.sz = ia.cap = 0;
            ia.a = nullptr;
        }
        std::cout << "move assignment" << std::endl;
        return *this;
    }

    T& operator[](int i) { return a[i]; }
    const T& operator[](int i) const { return a[i]; }

    int size() const { return sz; }
    int capacity() const { return cap; }

    void push_back(T x) {
        if (sz == cap) {
            // Separate cap*=2 to preserve invariant on exception
            // T *a2 = new T[cap *= 2];
            T *a2 = new T[cap * 2];
            cap *= 2;
            std::copy(a, a+sz, a2);
            delete[] a;
            a = a2;
        }
        a[sz++] = x;
    }

private:
    size_t sz;
    size_t cap;
    T *a;
};

template <typename T>
std::ostream& operator<<(std::ostream& os, const Vec<T>& ia)
{
    for (int i = 0; i < ia.size(); i++) {
        os << ia[i] << " ";
    }
    std::cout << "(cap=" << ia.capacity() << ")" << std::flush;
    return os;
}

using namespace std;

Vec<int> createVecInt()
{
    Vec<int> ia;
    for (int i = 0; i < 20; i++) {
        ia.push_back(i);
        cout << ia << endl;
    }
    return ia;
}

Vec<string> createVecStr()
{
    Vec<string> ia;
    for (char c = 'A'; c <= 'Z'; c++) {
        string s;
        s += c;
        ia.push_back(s);
        cout << ia << endl;
    }
    return ia;
}

int main() 
{
    // Vec<int> ia { createVecInt() };
    // Vec<string> ia2 { createVecStr() };

    vector<string> v; 
    for (char c = 'A'; c <= 'Z'; c++) {
        string s;
        s += c;
        v.push_back(s);
        for (const auto& e : v) {
            cout << e << " ";
        }
        cout << '\n';
    }


    // Lecture plan:
    //
    // 1. IntArray code walk-through
    //
    //     - ctor initializer syntax
    //     - =delete & =default
    //     - invariants
    //
    // 2. move ctor
    //
    //     - rvalue reference
    //     - -fno-elide-constructors
    //
    // 3. move assignment
    //
    //     - casting to rvalue reference
    //
    // 4. IntArray to Vec<T>
    //
    //     - Vec<int>
    //     - Vec<string>
    //
    // 5. vector<string>
    //
    //     - range for loop
}

to.cpp

#include <iostream>
#include <iomanip>
#include <sstream>

using namespace std;

/*
 * Type-covert Source to Target
 *
 *   - code from "A Tour of C++", Sec 11.7.3 (3rd ed.) / Sec 10.8 (2nd ed.)
 */
template <typename Target = string, typename Source = string>
Target to(Source arg) 
{
    stringstream interpreter; 
    Target result;

    if (!(interpreter << arg) ||           // write arg into stream
        !(interpreter >> result) ||        // read result from stream 
        !(interpreter >> std::ws).eof())   // stuff left in stream?
    {
        throw runtime_error{"to<>() failed"};
    }
    return result;
}

int main()
{
    cout << to(40'000) << '\n';

    cout << to<string,unsigned short>(80'000) << '\n';
}