254 lines
9.3 KiB
Markdown
254 lines
9.3 KiB
Markdown
# 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.
|
|
|
|
## 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.
|
|
|
|
See [Iterator Invalidation](https://en.cppreference.com/w/cpp/container#Iterator_invalidation).
|
|
|
|
## 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?
|
|
|
|
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).
|
|
|
|
## 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)).
|
|
|
|
## 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
|
|
|
|
## Task 08
|
|
|
|
You are given the following definition of a person:
|
|
|
|
```cpp
|
|
struct Person {
|
|
std::string firstname;
|
|
std::string lastname;
|
|
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`
|
|
|
|
## 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.
|
|
|
|
## 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`
|
|
- use a lambda expression
|
|
- use `std::mem_fn`
|
|
|
|
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`.
|
|
|
|
## 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)*.
|