2020-10-06 12:34:06 +02:00
# Exercises
## Task 01
Install G++ and Clang, then compile the provided file [`hello.cpp` ](task01/hello.cpp ).
Use the following flags when compiling:
-std=c++17 -Wall -Wextra -O2
Next, set up [Boost ](http://www.boost.org/ ) on your system and compile the provided file [`hello_boost.cpp` ](task01/hello_boost.cpp ).
Boost is quite common and provides you a set of useful C++ libraries.
Some of its content is even promoted into the C++ standard library.
## Task 02
Run Clang on the provided file [`vec.cpp` ](task02/vec.cpp ) using the following command:
clang -std=c++17 -Xclang -ast-dump -fsyntax-only -Wno-vexing-parse vec.cpp
Clang will parse the input file and display its abstract syntax tree (AST).
In the bottom half of the output you'll find the function declaration of `main` followed by its `CompoundStmt` .
Take a close look at its children and compare the resulting AST with the input code.
Notice any oddities — something that looks counter intuitive?
As you can see, there are multiple different ways of initialisation in C++.
Check out the [corresponding section at cppreference ](https://en.cppreference.com/w/cpp/language/initialization ).
## Task 03
The directory [`task03` ](task03 ) hosts four subdirectories, `libFoo` , `libBar` , `libBaz` , and `app` .
Each folder prefixed with `lib` represents a library and contains a header plus a source file.
Furthermore, the library `libBaz.so` depends on `libBar.so` .
`app` contains a single source file providing a `main` function.
It depends on all three libraries.
![Dependency Graph ](images/task03_dependencies.png )
- Model this project structure using [CMake ](https://cmake.org/ )
- Be sure to set the C++ standard to C++17 and enable warnings (`-Wall -Wextra`)
- The default build type should be *Release*
CMake itself is a build system generator.
You can choose from a variety of target build systems.
2020-10-22 12:46:23 +02:00
## Task 04
Examine the program [`iterations.cpp` ](task04/iterations.cpp ) and think about the expected output.
Compile the program and run it.
What do you notice?
Did you expect this behaviour?
Did you get any compiler warnings?
Investigate what is actually happening (consider using `valgrind` or a debugger).
How can such errors be prevented?
Look for tools (e.g. static code analysers) which help discovering such faulty code.
**Note:** If you run the executable and everything seems normal, try changing the initial content of `xs` , using different optimisation flags, or a different compiler.
The actual behaviour of this executable depends on various factors.
2020-10-23 10:18:03 +02:00
See [Iterator Invalidation ](https://en.cppreference.com/w/cpp/container#Iterator_invalidation ).
2020-10-22 12:46:23 +02:00
## Task 05
You are given the program [`strange.cpp` ](task05/strange.cpp ).
Compile it with different compilers and optimisation flags.
What do you notice?
What is really happening here?
2020-10-23 10:18:03 +02:00
See [Undefined Behaviour ](https://en.cppreference.com/w/cpp/language/ub ) and [Defining the undefinedness of C ](https://dl.acm.org/citation.cfm?id=2737979 ).
2020-10-30 10:29:47 +01:00
## Task 06
This task focuses on the correct implementation of RAII as well as copy and move semantics.
You are asked to implement the concept of `unique_ptr` and `shared_ptr` .
Since we won't concern ourselves with templates for the moment your implementation will *own* an instance of the following `struct` .
```cpp
struct Vec2 {
float x, y;
};
```
- Read the documentation regarding *smart pointers* , `unique_ptr` , and `shared_ptr`
- Implement your version of `unique_ptr_to_vec2` and `shared_ptr_to_vec2` fulfilling these requirements:
- *Dynamically* allocate an instance of `Vec2` in your constructor
- De-allocate the `Vec2` instance in your destructor
- Implement correct copy semantics (copy constructor / copy assignment)
- Implement correct move semantics (move constructor / move assignment)
- Enable access to `Vec2` via the operators `*` and `->`
- Thread-safety for `shared_ptr_to_vec2` 's reference counter is not required
- Pay attention to corner-cases like self-assignment (`v = v`)
- Prepare a few interesting test cases
- Check your implementation for memory leaks and memory corruptions using `valgrind` and sanitizers
See [Rule of Three ](https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming )).
2020-10-30 11:08:09 +01:00
## Task 07
Read [this blog post ](https://www.gamedev.net/blogs/entry/2265481-oop-is-dead-long-live-oop ).
- Pay attention to *implementation vs. interface inheritance*
- Pay attention to the use of templates (assuming you've already covered them)
- Think about the benefits and drawbacks of the used patterns
2020-11-06 01:32:43 +01:00
## Task 08
You are given the following definition of a person:
```cpp
struct Person {
2020-11-06 02:22:06 +01:00
std::string firstname;
std::string lastname;
2020-11-06 01:32:43 +01:00
int age;
};
```
- Implement relational operators (`< `, `<=` , `>` , `>=` )
- Implement comparison operators (`==`, `!=` )
Next, create 5 different instances and put all of them
- in an `std::vector` ;
- in an `std::set` ; and
- in an `std::map` as key (we don't care about the value type of the map).
Use algorithms from the standard library, like `std::find` and `std::partition` on these containers and examine which operators are used.
*Hint:* You may want to have a look at `std::tie` .
## Task 09
Reuse `Person` from Task 08 and implement the necessary parts for inserting it into an `std::unordered_set` .
Compare the performance of:
- `std::vector`
- `std::list`
- `std::set`
- `std::unordered_set`
2020-11-06 01:49:27 +01:00
## Task 10
Have a look at [this ](https://bollu.github.io/mathemagic/declarative/index.html ).
Now, do that in C++!
Utilize lambdas, `std::function` , and/or structs with call operators.
Critically think about ownership and minimize the amount of heap allocations.
## Task 11
Take a look at [Boost's chat server example ](https://www.boost.org/doc/libs/1_74_0/doc/html/boost_asio/examples/cpp11_examples.html#boost_asio.examples.cpp11_examples.chat ).
Try to understand how the session's lifetime is managed by the server.
Focus on `std::enable_shared_from_this` in combination with lambda captures.
2020-11-13 10:46:34 +01:00
## Task 12
Reuse `Person` from Task 08 and take the following, incomplete definition of a room:
```cpp
class Room {
public:
Room(int id, size_t limit) : id(id), limit(limit) {}
// Returns true iff the person successfully entered.
bool enter(/* Person */) {}
void exit(/* Person */) {}
private:
const int id;
const size_t limit;
std::vector< /* Person */> peopleInside;
};
```
`Room` contains a list of people currently located inside.
People can enter and exit the room via the respective member functions.
However, at most only `limit` people may be inside at any given time (invariant).
- Add the missing pieces, paying special attention to the types
The following use cases need to be covered next:
- Asking a `Room` how many people are currently located inside
- Asking a `Room` whether a specific person is currently located inside
- Iterating over all people currently located in a `Room`
Implement whatever is necessary to support these use cases, making sure the invariant remains intact.
## Task 13
Reuse `Person` from Task 08.
Create an `std::vector<std::shared_ptr<Person>>` with at least 3 different elements.
Create a function which takes a `const std::vector<std::shared_ptr<Person>>&` as input and returns an `std::vector<Person*>` .
Each element in the result vector corresponds to the respective element in the input vector.
For the functional programming nerds, the definition of this function would be something like `fmap std::shared_ptr::get` .
Write your function in different ways and compare the readability:
- use a range-based for loop
- use `std::transform`
2020-11-13 17:13:13 +01:00
- use a lambda expression
- use `std::mem_fn`
2020-11-13 10:46:34 +01:00
Think about taking the argument by value instead of taking it by const reference.
## Task 14
Implement your own version of `std::vector` without using any of the provided containers — use *regular arrays* (`new[]` / `delete[]` ) to house your elements.
The focus of this task lies on the use of templates and implementation of iterators.
You do not have to concern yourself with custom allocators.
Test your implementation with different types (`int`, `double` , and a custom struct).
Take your vector from task 1 and implement iterators.
You might want to read through the respective documentation.
Write some tests utilising algorithms provided by the standard library to check if your iterators behave correctly.
## Task 15
Take your vector implementation from Task 14 and instantiate it with a big number of unique types.
Inspect the relationship between the number of unique instantiates and compile time.
Furthermore, look at the compiled object file using `nm` .
2020-11-19 23:47:47 +01:00
## Task 16
In this task you have to create a rudimentary plugin system.
You are given `plugin.hpp` which contains an interface for your plugins, as well as the function name of the constructor function and its type.
Note that the constructor function returns an `std::unique_ptr<Plugin>` .
- create an executable which *dynamically* loads plugins and executes their `run` member function
- create two different plugins (`foo` and `bar` ) showing off the plugin system
It could look like this:
$ ./main ./libFoo.so
Creation of first plugin
Running the first plugin
Destruction of first plugin
$ ./main ./libFoo.so ./libBar.so
Creation of first plugin
Running the first plugin
Destruction of first plugin
Creation of second plugin
Running the second plugin
Destruction of second plugin
*Hint:* Have a look at the related man-pages *dlopen(3)* and *dlsym(3)* .