COMS W4995 C++ for C Programmers

Index of 2023-5/code/0613

Parent directory
Makefile
concept.cpp
itraits.cpp
stl3.cpp

Makefile

CC  = g++
CXX = g++

CFLAGS   = -Wall
CXXFLAGS = -Wall -std=c++17

executables = stl3 itraits

.PHONY: default
default: $(executables)

.PHONY: clean
clean:
	rm -f a.out core *.o $(executables)

.PHONY: all
all: clean default

concept.cpp

#include <array>
#include <vector>
#include <deque>
#include <list>
#include <forward_list>
#include <iostream>
#include <iterator>
#include <algorithm>
using namespace std;

void print_int(int x)  { cout << x << " "; }

template <bidirectional_iterator I>
void MyReverse(I b, I e)
{
    while ((b != e) && (b != --e)) {
        std::iter_swap(b,e);
        ++b;
    }
}

template <input_iterator I>
void MyReverse(I b, I e)
{
    throw invalid_argument("BidirectionalIterator required");
}

template <typename I>
void MyReverse(I b, I e)
{
    throw invalid_argument("You are not even trying...");
}

int main() {
    try {
        list v { 100, 101, 102, 103, 104 };
        //forward_list v { 100, 101, 102, 103, 104 };

        MyReverse(v.begin(), v.end());
        //MyReverse(100, 200);

        for_each(v.begin(), v.end(), &print_int);  cout << '\n';
    }
    catch (const exception& e) {
        cerr << e.what() << '\n';
    }
}

itraits.cpp

#include <array>
#include <vector>
#include <deque>
#include <list>
#include <forward_list>
#include <iostream>
#include <iterator>
#include <algorithm>

using namespace std;

// Pretty much the same as std::for_each
template <typename I, typename F>
void For_Each(I b, I e, F f)
{
    while (b != e) {
        f(*b);
        ++b;
    }
}

void print_int(int x)  { cout << x << " "; }

/*
template <typename I>
void MyReverse(I b, I e)
{
    while ((b != e) && (b != --e)) {
        std::iter_swap(b,e);
        ++b;
    }
}
*/

template <typename I>
void __doMyReverse(I b, I e, bidirectional_iterator_tag)
{
    while ((b != e) && (b != --e)) {
        std::iter_swap(b,e);
        ++b;
    }
}

template <typename I>
void __doMyReverse(I b, I e, forward_iterator_tag)
{
    throw invalid_argument("BidirectionalIterator required");
}

template <typename I>
void MyReverse(I b, I e)
{
    __doMyReverse(b, e, typename I::iterator_category());
    
    // The following change makes this work for C pointers as well because
    // iterator_traits<T*>::iterator_category = random_access_iterator_tag
    //
    // __doMyReverse(b,e,typename iterator_traits<I>::iterator_category())
}

int main()
{
    try {
        //list v { 100, 101, 102, 103, 104 };
        forward_list v { 100, 101, 102, 103, 104 };
        //deque v { 100, 101, 102, 103, 104 };

        MyReverse(v.begin(), v.end());
        //reverse(v.begin(), v.end());

        For_Each(v.begin(), v.end(), &print_int);  cout << "\n";
    }
    catch (const exception& e) {
        cerr << e.what() << '\n';
    }
}

/*

Lecture outline:

1. MyReverse() vs. std::reverse()

    - different levels of error reporting on forward_list

2. Introduction to Template Metaprogramming: tag dispatching

    - Container<T>::iterator_category type
    - why is deque<T>::iterator_category bidirectional_iterator_tag?
    - iterator_traits<I> and template specialization

3. Concepts in C++20

    - "template <bidirectional_iterator I>" instead of
      "template <typename I>"
*/

stl3.cpp

#include <array>
#include <vector>
#include <deque>
#include <list>
#include <forward_list>
#include <iostream>
#include <iterator>
#include <algorithm>

using namespace std;

template <typename Container>
struct IteratorThatPushes
{
    Container& c;

    using iterator_category = output_iterator_tag;
    using value_type = void;
    using difference_type = void;
    using pointer = void;
    using reference = void;

    explicit IteratorThatPushes(Container& c) : c(c) {}
    IteratorThatPushes& operator*()     { return *this; }
    IteratorThatPushes& operator++()    { return *this; }
    IteratorThatPushes  operator++(int) { return *this; }

    IteratorThatPushes& operator=(const typename Container::value_type& x)
    {
        c.push_back(x);
        return *this;
    }
};

// Pretty much the same as std::for_each
template <typename I, typename F>
void For_Each(I b, I e, F f)
{
    while (b != e) {
        f(*b);
        ++b;
    }
}

void print_int(int x)  { cout << x << " "; }

int main()
{
    vector<int> v1 { 100, 101, 102, 103, 104 };

    vector<int> v2;

    copy(v1.begin(), v1.end(), v2.begin());

    For_Each(v2.begin(), v2.end(), &print_int);  cout << '\n';
}

/*

Lecture outline:

1. copy(v1.begin(), v1.end(), v2.begin());

    - "For a container, use the ()-initializer syntax for sizes and the
      {}-initializer syntax for lists of elements" - Bjarne Stroustrup

code:

    vector<int> v2;
    vector<int> v2{5};
    vector<int> v2(5);


2. vector( std::initializer_list<T> init );


3. iterator that calls push_back()

code:

    vector<int> v2;
    copy(v1.begin(), v1.end(), IteratorThatPushes<vector<int>>{v2});
    copy(v1.begin(), v1.end(), IteratorThatPushes{v2});
    copy(v1.begin(), v1.end(), back_inserter(v2));


4. ostream_iterator

code:

    ostream_iterator<int> oi {cout, "\n"};
    copy(v2.begin(), v2.end(), oi);


5. istream_iterator

code:

    vector<int> v2;
    istream_iterator<int> iib {cin};
    istream_iterator<int> iie {};
    copy(iib, iie, back_inserter(v2));
    reverse(v2.begin(), v2.end());


5. Iterator categories

    - LegacyInputIterator
    - LegacyOutputIterator
    - LegacyForwardIterator
    - LegacyBidirectionalIterator
    - LegacyRandomAccessIterator
    - LegacyContiguousIterator (since C++17)
*/