 Parent directory
Parent directory
 Makefile
Makefile
 c2cpp_util.h
c2cpp_util.h
 functor.cpp
functor.cpp
 rand1.cpp
rand1.cpp
 rand2.cpp
rand2.cpp
CC  = g++
CXX = g++
CFLAGS   = -g -Wall
CXXFLAGS = -g -Wall -std=c++17
executables = functor rand1 rand2
.PHONY: default
default: $(executables)
functor: functor.o
rand1: rand1.o
rand2: rand2.o
rand2.o: c2cpp_util.h
.PHONY: clean
clean:
	rm -f *~ a.out core *.o $(executables)
.PHONY: all
all: clean default#include <cxxabi.h>
#include <typeinfo>
#include <cassert>
#include <cstdlib>
#ifdef __GNUC__
// use abi::__cxa_demangle to demangle type name in g++ & clang++
#define PRINT_TYPE(os, x) do {                                      \
    int status;                                                     \
    char *s = abi::__cxa_demangle(typeid(x).name(), 0, 0, &status); \
    assert(status == 0);                                            \
    os << s << '\n';                                                \
    std::free(s);                                                   \
} while (0)
#else
// MSVC returns human-readable type name, according to cppreference.com
#define PRINT_TYPE(os, x) do {                                      \
    os << typeid(x).name() << '\n';                                 \
} while (0)
#endif#include <array>
#include <vector>
#include <deque>
#include <list>
#include <forward_list>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <functional>
using namespace std;
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 << " "; }
struct Printer_of_int {
    void operator()(int x) const { cout << x << ' '; }
};
// From: https://en.cppreference.com/w/cpp/language/member_template
struct Printer {
    std::ostream& os;
    Printer(std::ostream& os) : os(os) {}
    // member function template
    template<typename T>
        void operator()(const T& obj) { os << obj << ' '; }
};
int main() {
    vector<int> v1 { 100, 101, 102, 103, 104 };
    vector<int> v2;
    copy(v1.begin(), v1.end(), back_inserter(v2));
    
    For_Each(v2.begin(), v2.end(), &print_int);  cout << '\n';
}
/*  Lecture outline:
1.  Function objects (aka functors)
    - operator()
    - Member function template
code:
    For_Each(v2.begin(), v2.end(), Printer_of_int{});  cout << '\n';
    For_Each(v2.begin(), v2.end(), Printer{cout});  cout << '\n';
2.  STL function objects
    - Stateless unary & binary functions
    - Binding argumnets with bind()
        - Use bind() to implennt f(x) = x + 300
        - std::placeholders::_1 represents the future argument
          that will be passed to the functor generated by bind()
    - Composing functions with bind()
        - USe bind() to implement g(x) = -x + 300
code:
    For_Each(v2.begin(), v2.end(), negate<int>());
    transform(v1.begin(), v1.end(), back_inserter(v2),
            negate<int>());
    transform(v1.begin(), v1.end(), back_inserter(v2),
            bind(plus<int>(), placeholders::_1, 300));
    transform(v1.begin(), v1.end(), back_inserter(v2),
            bind(plus<int>(),
                bind(negate<int>(), placeholders::_1),
                300));
3.  Lambda
    - Lambda expression
    - Capturing local variables to implement Closure
    - Type of lambda
code:
    transform(v1.begin(), v1.end(), back_inserter(v2),
            [] (int x) {
                return (-x + 300) * 10;
            }
            );
    double a = 2.0, b = 100;
    auto lambda = [&a, b] (int x) {
        return a * x + b;
    };
    a = 4.0, b = 1'000'000;
    transform(v1.begin(), v1.end(), back_inserter(v2), lambda);
    struct Lambda1 {
        double& a;
        double  b;
        Lambda1(double& a, double b) : a(a), b(b) {}
        double operator()(int x) const { return a*x+b; }
    };
    Lambda1 lambda{a, b};
*/#include <cstdlib>
#include <cmath>
#include <iostream>
#include <iomanip>
#include <string>
#include <map>
#include <random>
#include <functional>
using namespace std;
int main() {
    // Random number generator using C functions
    srandom(time(nullptr)); // current time as seed
    auto engine = &random; // random() in the C library as engine
    auto distro = [] (auto some_engine) {
        // get an unsigned random integer from engine,
        // and "distribute" it in [-10,10]
        return some_engine() % 21 - 10;
    };
    // Random number generator in C++
    /*
    random_device rd;
    random_device::result_type seed;
    seed = (rd.entropy() > 0) ? rd() : time(nullptr);
    default_random_engine engine { seed };
    uniform_int_distribution<> distro { -10, 10 };
    */
    map<int,int> m;
    for (int i = 0; i < 1'000'000; ++i) {
        int x = distro(engine);
        ++m[x];
    }
    for (const auto& e : m) {
        cout << setw(4) << e.first << '|' << e.second << '\n';
    }
}
/*  Lecture outline
1.  Random number generation in C - code walk-thru
    - Basically calling random() but arranged in C++ style
    - Linear congruential generator: ( a * x + b ) % c
    - Seeding the generator with current time
    - random() is nonlinear and has a large period
2.  Random number generation in C++ - code walk-thru
    - std::random_device
        - implementation-dependent non-deterministic random number generator
        - random_device::entropy() returns 0 if it's deterministic
        - uses /dev/urandom in Linux
    
    - STL provides numerous engines and distributions
*/#include <cstdlib>
#include <cmath>
#include <iostream>
#include <iomanip>
#include <string>
#include <map>
#include <random>
#include <functional>
#include "c2cpp_util.h"
using namespace std;
int main() {
    random_device rd;
    random_device::result_type seed;
    seed = (rd.entropy() > 0) ? rd() : time(nullptr);
    //default_random_engine engine { seed };
    //uniform_int_distribution<> distro { -10, 10 };
    mt19937 engine { seed };
    normal_distribution<> distro { 0.0, 3.5 };
    map<int,int> m;
    int maximum = 0;
    for (int i = 0; i < 1'000'000; ++i) {
        int x = lround( distro(engine) );
        int occurence = ++m[x];
        maximum = max(maximum, occurence);
    }
    int bar_scale = maximum / 50;
    for (const auto& e : m) {
        cout << setw(4) << e.first << '|' 
            << setw(7) << e.second << '|'
            << string(e.second / bar_scale, '*') << '\n';
    }
}
/*  Lecture outline:
1.  Generating normal-distributed random numbers
    - normal_distribution<> distro { 0.0, 3.5 };
    - code walk-thru
2.  Mersenne Twister engine
    - mt19937 and mt19937_64 are predefined specializations of the
      mersenne_twister_engine class template
    - Mersenne Twister 
        - reference: https://en.wikipedia.org/wiki/Mersenne_Twister
        - its period length is a Mersenne prime, 2^19937 - 1
        - fast, but heavy state storage requirement
3.  (A little detour to the) Danger of narrowing conversion
    - Subtle bug without lround()
        int x { distro(engine) }; // we get a compiler warning at least
        int x = distro(engine);   // we don't even get a warning here
4.  Stateful functor
    - Binding engine to distro:
        //int x = lround( distro(engine) );
        auto generator = bind(distro, engine);
        int x = lround( generator() );
        - Why the same number million times?
    - We can fix it by using std::ref():
        auto generator = bind(distro, ref(engine));
        - This will make bind() hold the engine by reference.
          We'll study the implementation of std::ref() later.
    - Capturing distro and engine in a lambda
        auto generator = 
            //[distro, engine] () { return distro(engine); };           // (1)
            //[distro, engine] () mutable { return distro(engine); };   // (2)
            //[=] () mutable { return distro(engine); };                // (3)
            //[&distro, &engine] () { return distro(engine); };         // (4)
            //[&] () { return distro(engine); };                        // (5)
        int x = lround( generator() );
        - (1) does not compile - lambda's operator() is const by default
        - (2) mutable makes operator() non-const - same as non-working bind
        - (3) is the same as (2); "=" means capture all variables by value
        - (4) and (5) capture by reference, and fix the problem
    - Better yet, construct the generator object outside the loop!
        // The generator object should be constructed outside the loop.
        auto generator = bind(distro, engine);
        for (int i = 0; i < 1'000'000; ++i) {
5.  VERY stateful functor
    - Why was it so slow when engine was being copied in the loop?
        cout << '\n' << typeid(engine).name() << "\n\n";
        PRINT_TYPE(cout, engine);
        cout << "\nsizeof(engine): " << sizeof(engine) << "\n\n";
*/