C++ Workshop
============
---
Contents
--------
[Intro](#intro)
[Basics](#basics)
[STL](#stl)
[Memory](#memory)
[Classes](#classes)
[Templates](#templates)
[Exceptions](#exceptions)
[Style](#style)
---
Intro
=====
---
What is C++? (practical level)
-----------------------------------
- C-like syntax
- _C programs can be compiled as C++_
- Supports Object Oriented Programming
- _like structs with functions_
- _C has great performance, but it is missing OO_
- Standard Template Library (STL)
- _pre-written classes and algorithms eg._ `std::list` or `std::sort`
----
What is C++? (higher level)
-----------------------------------
- High performance
- Aims to have Zero Cost Abstractions
- Portable
- Runs on many devices eg. x86, ARM, Arduino, Comodore64
- Robust
- Strong type system & value semantics
- RAII (scope) & strict resource ownership
- Fairly expressive
- How clearly intent can be expressed in code
- How closely it follows spoken language
----
Not very expressive
-------------------
```C
// C version
struct string_elem_t { const char* str_; string_elem_t* next_; };
int num_hamlet(string_elem_t* books) {
const char* hamlet = "Hamlet";
int n = 0;
string_elem_t* b;
for (b = books; b != 0; b = b->next_)
if (strcmp(b->str_, hamlet) == 0)
++n;
return n;
}
```
----
Fairly expressive
-----------------
```C++
// C++ version
int num_hamlet(const std::list<std::string>& books) {
return std::count(books.begin(), books.end(), "Hamlet");
}
```
----
Very expressive
---------------
```python
def numHamlet(books):
return len(b for b in books if b == 'Hamlet')
```
----
What is C++? (corollary)
-----------------------------------
- Very flexible so can be _unsafe_ if not coded with standards and practices in mind
- Highly backwards compatible
- As practices have evolved, lots has been added
- Added difficulty: knowing which are best current practices
- Fair amount of syntax and concepts to learn
---
Hello World now in C++
----------------------
```cpp
#include <iostream> // cout
int main() {
std::cout << "Hello World\n";
return 0;
}
```
:::info
Compared to Python, there are `{` curlys `}` and semi-colons `;`
:::
----
Hello World C++ using namespace
--------------------------------
```cpp
#include <iostream> // cout
using namespace std; // ADDED
int main() {
cout << "Hello World\n"; // CHANGED: removed `std::`
return 0;
}
```
:::warning
This is discouraged in modern C++ style because it _pollutes the namespace_
:::
---
C++ file extensions
-------------------
- `.cpp` for implementation
- `.h` or `.hpp` for header files (interface)
---
How to compile?
---------------
```shell
rez env gcc
g++ -Wall -Wextra -pedantic -std=c++14 -O2 -o hello hello.cpp
```
Online compiler here: [wandbox.org](https://wandbox.org)
----
How to compile with clang?
---------------
```shell
rez env clang
clang++ -Wall -Wextra -pedantic -std=c++14 -O2 -o hello hello.cpp
```
:::info
Clang can provide nicer and coloured errors
:::
:::warning
`g++` and `clang++` binaries may have differences
:::
:::info
`gcc` and `g++` are aliased to Clang on macOS
:::
---
Basics
======
---
Key Terminology
---------------
- __Declaration__: _Defining of entity name, type (and perhaps parameters)_
- __Definition__: _Assigning value/instructions to a declared entity_
:::info
In Python variables (and their types) don't need to be declared before use, only defined.
:::
----
```C++
int main() {
int number; // declaration (contains undefined random number)
number = 42; // definition
int countdown = 10; // both at once
return 0;
}
```
:::warning
Best to always do both at once to avoid accidents.
:::
:::info
Recommended: declare and define variables close to first use.
:::
---
Scope
-----
- __Scope__: _Part of a program where a name is valid_
- A scope begins from its point of declaration
- ...and ends at the end of the block (at the `}`)
- 4 types: local, function, namespace, class
- Restricting scope helps _reason_ about a piece of code
----
Local Scope
-----------
```cpp
// How many local scopes exist here?
void foo() {
int i = 42;
for (int i = 0; i < n; ++i) {
// do some stuff
}
if (i < 53) {
// do some stuff
}
{ int i; }
}
```
----
:::danger
Try and avoid "_name hiding_" - that is, redeclaring a variable with a name that already exists in local scope.
:::
----
Local Scope
-----------
```cpp
// How many local scopes exist here?
void foo() {
if (true) {
int i = 42;
else {
int i = 53;
}
std::cout << i << '\n'; // ERROR - no i in scope
}
```
:::info
This is different from python which has function scope (and is _garbage collected_)
:::
----
Function Scope
--------------
Prototype declarations and definitions
```cpp
void swap(int a, int b); // prototype
int main() {
int a = 42;
int b = 53;
swap(a, b); // will this work?
}
void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}
```
----
Namespace Scope
---------------
```cpp
namespace Fruit {
std::string name;
}
namespace Vegetable {
std::string name;
}
void baz() {
std::cout << Fruit::name;
std::cout << Vegetable::name;
std::cout << name; // error
}
```
----
Class Scope
-----------
See the _[Classes](#classes)_ section
---
`const`
-----
```cpp
int a; // normal integer.
const int b; // integer that can't be modified.
const int* c; // pointer to a const integer.
// The value "c" points to cannot be modified.
int* const d; // const pointer to an integer.
// "d" cannot be modified to point elsewhere.
const int* const e; // const pointer to a const integer.
// The value "e" points to cannot be modified.
// "e" cannot be modified to point elsewhere.
```
- The trick is to read it backwards
- "`e` is a `const` _pointer_ to an `int` that is `const`"
----
```cpp
int initial = 42;
int changeTo = 1;
const int b = 0;
b = 3; // ERROR
const int* c = &initial;
*c = changeTo; // ERROR
c = &changeTo; // ok
```
- `*` after type means _pointer to a variable of that type_
- `*` before variable use means _dereference_
- "_go to and use the variable at this address_"
----
```cpp
int initial = 42;
int changeTo = 1;
int* const d = &initial;
*d = changeTo; // ok
d = &changeTo; // ERROR
const int* const e = &initial;
*e = changeTo; // ERROR
e = &changeTo; // ERROR
```
---
Casting (Explicit Type Conversion)
-------
- Implicit type conversions happen all the time.
```cpp
double s = 2; // an integer upcast to a double
// s == 2.0
int t = 2.5; // a double downcast to an int
// t == 2
```
- However sometimes implicit casts don't work.
----
- What is wrong with below?
- What is the value of result?
_C-style cast_
```c
double result = rand()/(double)RAND_MAX;
```
:::warning
C-style casts are hard to "search", and generalise different kinds of casting. <!-- .element: class="fragment" data-fragment-index="1" -->
:::
----
`static_cast<T>`
--------------
Allows for value-casting for related types.
```cpp
double result = rand()/static_cast<double>(RAND_MAX);
```
:::info
Static casting occurs at compile time. This reduces odds of runtime bugs.
:::
----
`reinterpret_cast<T>`
-------------------
Keeps all the bits the same, and gives it a new label.
```cpp
void* myPointer = 0x3894AEBB
int myInteger = static_cast<int>(myPointer); // Not related, fail
int myInteger = reinterpret_cast<int>(myPointer);
// This succeeds, as myInteger is the integer value of the
// address of the myPointer pointer
int myInteger = (int)myPointer; // same as re-interpret cast
```
:::info
Reinterpret Cast is what C-style casts "fall back" on / assumed to do.
:::
----
`const_cast<T>`
-------------
Removes the `const`-ness of a type
```cpp
const int light_intensity = 300;
const_cast<int>(light_intensity) = 400;
```
:::danger
This should be used with CAUTION. Things are usually const for a reason!
:::
---
References
----------
Let's start with a basic example:
```cpp
int i = 1;
int& myref = i;
// The following have the same effect
i = 2;
myref = 2;
```
:::info
`myref` is just another name for object `i`
:::
- `&` after a type means "_reference_"
- `&` before variable (when there is no type) means "_address of_"
----
References - Key Points
-----------------------
- References <b>must</b> be defined when declared
- References are always const (since it can't be redefined)
```cpp
// non-const reference: Reference to non-const object
int i = 3;
int &nonconstref = i;
// const reference: Reference to const object
const int i = 3;
int &constref = i;
```
:::success
References are safer because they can NEVER be null
:::
---
STL
===
---
Standard Template Library
-------------------------
> Provides data-structures and algorithms so you don't have to reinvent the wheel
:::info
Everything in STL is in the `std` namespace
:::
---
Browsing Documentation
--------------------------
- [cppreference.com](http://en.cppreference.com/w/cpp)
- [devdocs.io](https://devdocs.io/cpp/)
----
Documentation Viewer: Zeal (linux)
----------------------------------
### [zealdocs.org](https://zealdocs.org)
![screenshot](https://i.imgur.com/v63u1ZJ.png)
- offline mode
- fuzzy search
- free & open source
----
Documentation Viewer: Dash (macOS)
----------------------------------
### [kapeli.com/dash](https://kapeli.com/dash)
![](https://i.imgur.com/EHOOVSl.png)
- offline mode
- fuzzy search
- not free :disappointed: (but has limited free version)
----
Documentation Viewer: Velocity (windows)
----------------------------------------
### [velocity.silverlakesoftware.com](https://velocity.silverlakesoftware.com)
- offline mode
- fuzzy search
- free
---
`#include`-ing libraries
----------------------
- C++ builtin header files don't need a `.h`
- `#include <list>`
- Old C libraries are available, prefix with a `c`
- `#include <cstdlib>`
- `#include <cassert>`
- Your own header files still need the extension
- `#include "utils/Timer.hpp"`
- `#include <boost/algorithm/string.hpp>`
---
STL Containers
--------------
- __[vector](#stdvector)__ _a dynamic array_
- __[list](#stdlist)__ _a linked list_
- __stack__ _first in, last out_
- __queue__ _first in, first out_
- __priority_queue__ _an always sorted queue_
- __deque__ _a queue and also a stack_
- __set__ _an ordered set (red-black tree)_
- __map__ _(key : value) store - like `dict` in python_
---
`std::string`
-------------
- `std::string` abstracts away (C-like) `char` arrays.
```cpp
#include <iostream>
#include <string>
int main() {
std::string greeting = "Hello there!";
std::cout << greeting << '\n';
return 0;
}
```
----
```cpp
#include <iostream>
#include <string>
int main() {
using std::string;
string greeting = "Hello there!";
std::cout << greeting << '\n';
return 0;
}
```
----
_fruit.hpp_
```cpp
const std::string fruits[] = {
"Apple",
"Pear",
"Orange",
"Banana",
//...
"Mango",
"Apricot",
};
```
---
`std::list`
-----------
- `std::list` is a generic linked list implementation
```cpp
#include <list>
#include <string>
int main() {
using std::list;
int numbers[] = {2, 3, 5, 7};
list<int> primes = list<int>(numbers, numbers + 4); // not good practice
int names[] = {"BB8", "Chewie", "Han", "R2D2"};
list<std::string> characters = list<std::string>();
for (const auto& name : names) {
characters.push_back(name);
characters.push_back("Rey");
characters.push_front("Yoda");
return 0;
}
```
----
:::warning
Avoid pointer arithmetic (`numbers`)
STL Arrays are preferred because they store the size
:::
---
Iterating through an STL container
----------------------------------
![](https://i.imgur.com/FCcDfN4.png)
----
_ObservedPostHistory.cpp_
```cpp
void ObservedPostsHistory::printHistory() const {
using std::list;
list<ObservedPostSide>::const_iterator it = posts_.begin();
std::cout << "history = ";
while (it != posts_.end()) {
std::cout << *it << " ";
++it;
}
std::cout << std::endl;
}
```
----
Variation using a `for` loop
_ObservedPostHistory.cpp_
```cpp
void ObservedPostsHistory::printHistory() const {
std::cout << "history = ";
std::list<ObservedPostSide>::const_iterator it;
for (it = posts_.begin(); it != posts_.end(); ++it) {
std::cout << *it << " ";
}
std::cout << "\n";
}
```
:::success
With a `for` loop you can't forget to increment
:::
----
Rewritten using the `std::copy` algorithm
_ObservedPostHistory.cpp_
```cpp
void ObservedPostsHistory::printHistory() const {
std::cout << "history = ";
std::copy(posts_.cbegin(), posts_.cend(),
std::ostream_iterator<ObservedPostSide>(std::cout, " "));
std::cout << "\n";
}
```
:::success
With algorithms incrementing is hidden - you can't accidentally read an invalid index.
:::
---
`std::vector`
-------------
- A dynamic array implementation
- It resizes for you!
```cpp
int main() {
std::string tmp[] = {"don't", "push", "me",
"I'm", "close", "to", "the", "edge"};
std::vector<std::string> the_messages;
for (int i = 0; i < 8; ++i) {
the_messages.push_back(tmp[i]);
}
}
```
---
STL Algorithms
--------------
- STL Algorithms take container iterators (`.begin()` and `.end()`)
- Complete list at [cppreference.com/w/cpp/algorithm](http://en.cppreference.com/w/cpp/algorithm)
---
`std::find`
-----------
- Returns an iterator (pointer) pointing to the element if found, otherwise the `.end()`
```cpp
int main() {
using namespace std;
std::vector<string>::const_iterator result;
string tmp[] = {"hay", "hay", "hay", "needle", "hay", "hay"};
vector<string> h_stack = vector<string>(tmp, tmp + 6);
result = find(h_stack.cbegin(), h_stack.cend(), "needle");
if (result != h_stack.end()) { cout << *result << "\n"; }
return 0;
}
```
---
`std::sort`
-----------
- Sorts elements in a container
_MultiGaussianDistribution.cpp_
```cpp
void MultiGaussianDistribution::fixupDistribution(void) {
std::sort(modes.begin(), modes.end(), GuassianSortFunction());
normaliseDistribution(modes);
mergeSimilarModes(modes);
std::sort(modes.begin(), modes.end(), GuassianSortFunction());
normaliseDistribution(modes);
removeUnlikelyModes(modes);
removeExcessModes(modes, maxGaussians);
normaliseDistribution(modes);
}
```
---
`std::for_each`
---------------
- Maps a function to a range of elements
```cpp
void greySky() {
void makeBrown(Leaf l) { l.setColor("brown"); }
std::vector<Leaf> leaves = std::vector<Leaf>(66, Leaf());
std::for_each(leaves.begin(), leaves.end(), makeBrown());
}
```
---
Boost
-----
- extended libraries in the `boost::` namespace
- documentation at [boost.org](http://www.boost.org/doc/libs/1_62_0/libs/libraries.htm) is your friend
- also [theboostcpplibraries.com](https://theboostcpplibraries.com/the-boost-c++-libraries)
----
Boost Example: splitting on whitespace
```cpp
#include <boost/algorithm/string.hpp> // split, is_space
// ...other #includes
int main() {
std::string text;
std::vector<std::string> results;
std::cout << "Enter some text to split:" << '\n';
std::getline(std::cin, text);
boost::split(results, text, boost::is_space());
for (size_t i = 0; i < results.size(); ++i) {
std::cout << results[i] << '\n';
}
}
```
---
Memory
======
---
Stack and Heap memory
---------------------
- __Stack__: _Named Memory_
- __Heap__: _Unnamed Memory_
----
Stack Memory
------------
```cpp
int doFoveaThings() {
Fovea myFovea;
myFovea.setPixel(1, 1);
myFovea.getPixel(1, 1);
// ~myFovea() is called
}
```
----
Heap Memory (`new` and `delete`)
--------------------------------
```cpp
int doFoveaThings() {
Fovea* myFovea = new Fovea();
myFovea->getPixel(1, 1);
// memory leak :O :/ :(
}
```
:::warning
It is strongly recommended to use [smart pointers](https://youtu.be/UOB7-B2MfwA) instead of `new` and `delete` because they have ownership control (safer)
:::
----
Heap Memory
------------
```cpp
int doFoveaThings() {
Fovea* myFovea = new Fovea();
myFovea->setPixel(1, 1);
myFovea->getPixel(1, 1);
delete myFovea;
}
```
----
Memory with array types
-----------------------
```cpp
int doFoveaThings() {
Fovea myFoveaList[5];
myFovea[0]->setPixel(1, 1);
myFovea[0]->getPixel(1, 1);
}
```
```cpp
int doFoveaThings() {
Fovea* myFovea = new Fovea[5];
myFovea[0].setPixel(1, 1);
myFovea[0].getPixel(1, 1);
delete[] myFovea;
}
```
---
Classes
=======
- Classes allow for Object Oriented programming.
----
- We want to create a Robot class. In C we might have:
_Robot.c_
```c
struct Robot {
int serial_number;
float version;
};
```
----
- To make it a class, just replace `struct` with `class`. This makes everything `private`.
_Robot.cpp_
```cpp
class Robot {
int serial_number;
float version;
};
```
----
- Let's add legs. `Leg` is another class that we will write.
_Robot.cpp_
```cpp
class Robot {
Leg left;
Leg right;
int serial_number;
float version;
};
```
:::warning
Note the semicolon. `g++` will complain without it.
:::
----
- In C we could _abstract_ (hide) the implementation. In C++ we use `public` and `private`.
- We also made a public _method_ called walk.
_Robot.cpp_
```cpp
class Robot {
public:
void walk(int steps) {
for (int i = 0; i < steps; ++i) {
left.step();
right.step();
}
}
private:
Leg left;
Leg right;
int serial_number;
float version;
};
```
----
```cpp
int main() {
Robot bb8 = Robot{}; // uniform initialisation on the stack
while (true) { bb8.walk(4); } // can only access public methods from here
return 0;
}
```
----
- We can put _method declarations_ in the class body and put the _definitions_ outside.
- This makes it easier to read the class interface.
_Robot.cpp_
```cpp
class Robot {
public:
void walk(int steps);
private:
Leg left;
Leg right;
int serial_number;
float version;
};
void Robot::walk(int steps) {
for (int i = 0; i < steps; ++i) {
left.step();
right.step();
}
};
```
----
- If we do that, we can seperate the code, putting the interface in a `.h` header file.
_Robot.h_
```cpp
class Robot {
public:
void walk(int steps);
private:
Leg left;
Leg right;
int serial_number;
float version;
};
```
----
- Type goes first, then the name of the class (like a namespace).
_Robot.cpp_
```cpp
void Robot::walk(int steps) {
for (int i = 0; i < steps; ++i) {
left.step();
right.step();
}
};
```
----
_Leg.h_
```cpp
class Leg {
public:
void move();
private:
Sensor toe_bumper;
Motor ankle;
};
```
---
Inheritance
-----------
_base class_
```cpp
class Cake {
protected:
int radius_; // in cm
void bake() { baked_ = true; }
void applyIcing() { hasIcing_ = true; }
public:
Cake() { radus_ = 20; has_icing_ = false; }
virtual void cook { bake(); applyIcing(); }
privatej
bool hasIcing_;
bool baked_;
};
```
----
_sub class_
```cpp
class CupCake : public Cake {
public:
CupCake() {
radius_ = 5; // can modify because it is "protected"
}
virtual void cook() { bake(); applyIcing(); }
};
```
----
_sub sub class_
```cpp
class BlueberryCupCake : public CupCake {
public:
CupCake() {
radius_ = 5; // can modify because it is "protected"
}
void addBlueberries(int n_blueberries) {
for (int i = 0; i < n_candles; ++i) {
blueberries_.push_back(FreshBlueberry()); // Polymorphism: LSP
}
}
virtual void cook() { addBlueberries(); bake(); applyIcing(); }
private:
std::vector<Blueberry> blueberries_;
};
```
----
_sub class_
```cpp
class BirthdayCake : public Cake {
public:
BirthdayCake() {
radius_ = 25; // can modify because it is "protected"
}
virtual void cook() { bake(); applyIcing(); addCandles(); }
void addCandles(int n_candles) {
for (int i = 0; i < n_candles; ++i) {
candles_.push_back(Candle());
}
}
private:
std::vector<Candle> candles_;
};
```
---
Subtype Polymorphism
--------------------
- __Liskov Substitution Principle__: _Every sub class should be substitutable for its base class_.
- [more info on OO Design Principles](https://scotch.io/bar-talk/s-o-l-i-d-the-first-five-principles-of-object-oriented-design)
- __Contravariance__: _Parameters of overriding functions should be the same or a sub class_.
- __Covariance__: _Return type of the overriding function should be the same or a parent class_.
----
```cpp
class Person {
public:
void makeAndEatCake(Cake c) {
c.cook();
isFull = true;
}
private:
bool isFull;
}
```
```cpp
int main() {
Person brad = Person();
CupCake c = CupCake();
brad.makeAndEatCake(c);
}
```
----
- Sub class `CupCake` is substitutable where base class `Cake` is expected.
- Think of it as the sub class has some extra stuff (member variables and methods)
- The subclass has all the base class stuff too
- Anything you can do to the subclass, you can also do to the base class.
---
Templates
=========
---
Generic Programming
-------------------
Generalising software to be independent of type (e.g. `int`, `double`)
STL is an exmaple of generic programming `vector<int>`
---
Function Templates - Why?
------------------
```cpp
int min(int a, int b) {
if (a < b) {
return a;
}
return b;
}
```
```cpp
double min(double a, double b) { // a function overload
if (a < b) {
return a;
}
return b;
}
```
----
We can replace that with:
```cpp
template <typename T>
T min(T a, T b) {
if (a < b) {
return a;
}
return b;
}
```
```cpp
int main() {
int i1 = 1;
int i2 = 2;
double d1 = 1.0;
double d2 = 2.0;
std::cout << min<int>(i1,i2);
std::cout << min<double>(d1, d2);
}
```
----
What is wrong here
```cpp
int main() {
int i1 = 1;
int i2 = 2;
double d1 = 1.0;
double d2 = 2.0;
std::cout << min<int>(i1, d2);
}
```
----
What is wrong here?
```cpp
template <typename U,V,W>
W min(U a, W b) {
if (a < b) { return a };
return b;
}
```
---
Partial Specialisation
----------------------
- Pointers are different types, so we need to redefine our generic function or class for them as well.
```cpp
template <typename T>
T* min(T* a, T* b) {
if (*a < *b) { return *a; }
return *b;
}
```
```cpp
int main() {
int* i1 = 1;
int* i2 = 2;
double* d1 = 1.0;
double* d2 = 2.0;
std::cout << min(i1,i2);
std::cout << min(d1, d2);
}
```
---
Explicit Specialisation
-----------------------
- With every rule there is exceptions. Explicit specialisation is how we make these exceptions.
- Let's assume we want to re-define how the minimum string is found.
----
```cpp
// Define minimum not as least alphabetically, but as minimum number of characters
template <typename T>
std::string min(std::string a, std::string b) {
if (a.size() < b.size()) {
return a;
} else {
return b;
}
}
```
```cpp
int main() {
std::string s1 = "Apple";
std::string s2 = "Zoo";
std::cout << min(s1, s2);
}
```
---
Class Templates
---------------
```cpp
template <typename T>
class SubImage {
public:
SubImage(int width, int height);
T getPixel(int row, int column);
void setPixel(int row, int column, T);
int getHorizontalCount();
int getVerticalCount();
private:
vector<vector<T> > pixels;
int horizontal_start;
int horizontal_end
int vertical_start;
int vertical_end;
}
```
----
```cpp
int main() {
Rgb examplePixel(255,255,255);
SubImage* potentialBall = new SubImage(100,100);
potentialBall.setPixel(0,0,examplePixel);
}
```
---
Exceptions
==========
---
Exception
---------
- __Exception__: A run-time anomoly (a runtime error!)
- __Exception-handling__: Run-time mechanism to communiate exceptions between two unrelated parts of code
---
`try`/`catch`
---------
```cpp
int input;
try {
if (input > 100) {
throw 100;
}
} catch (100) {
// Caught - print out error
}
// Code continues
```
If an exception is thrown and not caught, the program terminates (this can be ok).
:::warning
It is good for errors to be visible and loud, rather than silently failing
:::
----
Types of throws
---------------
You can throw any type in C++, whether it is a primitive type:
```cpp
int input;
try {
if (input > 100) {
throw 100;
}
} catch (int& i) {
// Caught - print out error
}
// Code continues
```
----
Types of throws
---------------
Or a class object
```cpp
int input;
try {
if (input > 100) {
throw new RuntimeException;
}
} catch (RuntimeException& e) {
// Caught - print out error
std::cout << "Error: " << e << std::endl;
}
// Code continues
```
---
Exception Hierarchy
-------------------
- Exceptions allow for the use of Polymorphism.
- For example if BallDetectionError and GoalDetectionError are a sub-class of VisionError:
```cpp
try {
if (/*something wrong with ball*/) {
throw new BallDetectionError();
}
if (/*something wrong with*/) {
throw new GoalDetectionError();
}
} catch (VisionError& e) {
// Catch any vision-based error
}
// Code continues
```
----
Catching anything, `"..."`
--------------------------
- Sometimes we want to catch ANYTHING that can be thrown
```cpp
int input;
try
// throw anything, doesn't matter what it is
} catch (...) {
// Catch anything - print out error
}
// Code continues
```
---
Rethrow
-------
- After we catch a thrown exception, and we "clean up" sometimes we want to throw it again to the layer above.
```cpp
template <typename T>
class Image2D {
public:
Image2D(std::string name) { this.name = name };
T& getItem(int row, int column);
std::string& getName();
private:
vector<vector<T> > v;
vector<std::pair<int,int> > failedIndexLookup;
std::string name;
}
```
----
```cpp
Image2D::getItem(int row, int column) {
try {
T item = this.v.get(row).get(column);
} catch (std::out_of_range& e) {
this.failedIndexLookup.push_back(std::make_pair(row,column));
throw;
}
}
void foo() {
Image2D<Fovea> nvf;
try {
nvf.get(1,2);
} catch (std::logic_error) {
std::cerr << "Logic error in lookup" << std::endl;
}
}
```
----
```cpp
Image2D::getItem(int index) {
try {
T item = this.v.get(index);
} catch (std::out_of_range& e) {
this.failedIndexLookup.push_back(index);
throw;
} catch (...) {
throw;
}
}
```
---
Preventing Memory Leaks
-----------------------
```cpp
Image2D::getItem(int index) {
int* modifiedIndex = new int;
*modifiedIndex = index - FRAME_OFFSET;
try {
T item = this.v.get(modifiedIndex);
} catch (std::out_of_range& e) {
this.failedIndexLookup.push_back(modifiedIndex);
throw;
} catch (...) {
throw;
}
delete modifiedIndex;
}
```
---
Preventing Memory Leaks
-----------------------
```cpp
Image2D::getItem(int index) {
int* modifiedIndex = new int;
*modifiedIndex = index - FRAME_OFFSET;
try {
T item = this.v.get(modifiedIndex);
} catch (std::out_of_range& e) {
this.failedIndexLookup.push_back(modifiedIndex);
delete modifiedIndex;
throw;
} catch (...) {
delete modifiedIndex;
throw;
}
delete modifiedIndex;
}
```
:::warning
Remember to free all heap memory you've allocated in your catch statements
:::
---
That's all folks!