Parent directory
Makefile
c2cpp_util.h
functor.cpp
rand1.cpp
rand2.cpp
CC = g++
CXX = g++
CFLAGS = -Wall
CXXFLAGS = -Wall -std=c++17
executables = functor rand1 rand2
.PHONY: default
default: $(executables)
.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;
// 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 << " "; }
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) {}
// template member function
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()
- template member function
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()
- std::placeholders::_1 represents the future argument
that will be passed to the functor generated by bind()
- Composing functions with bind()
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
- 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);
*/
#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';
}
}
#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 };
2. Mersenne Twister engine
- mt19937 engine { seed };
3. Danger of narrowing conversion
- subtle bug without lround()
int x { distro(engine) }; // compiler warning
int x = distro(engine); // no warning, but subtle bug!
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?
- capturing distro and engine in a lambda
auto generator =
bind(distro, engine);
//bind(distro, ref(engine));
//[distro, engine] () { return distro(engine); };
//[distro, engine] () mutable { return distro(engine); };
//[=] () mutable { return distro(engine); };
//[&] () { return distro(engine); };
int x = lround( generator() );
- better yet, construct generator 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";
*/