Fix admonitions in Karel and add C++ placeholders (#4)

This commit is contained in:
Matej Focko 2023-11-24 18:31:19 +01:00 committed by GitHub
commit c06941d75a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 305 additions and 21 deletions

View file

@ -79,12 +79,15 @@ You can see an example of such map here:
As a first step write down any ideas and things that you have noticed or came to As a first step write down any ideas and things that you have noticed or came to
your mind. Ideally: your mind. Ideally:
- Write down a nested list of the problems, e.g. 1. Write down a nested list of the problems
2. Write down list of problems that can happen
3. Write down **anything** you consider important to solving the problem
:::info Example :::tip Example
Problem: I want to find out whether the display on smartphone should rotate. **Problem**: I want to find out whether the display on smartphone should rotate.
- nested list of problems
- Check if display has been rotated - Check if display has been rotated
- Read data from some sensor - Read data from some sensor
- From what sensor - From what sensor
@ -92,25 +95,11 @@ your mind. Ideally:
- How do I communicate with the sensor? - How do I communicate with the sensor?
- What is the meaning of the data that I got? - What is the meaning of the data that I got?
- How can I process it? - How can I process it?
- any problems that can happen
:::
- Write down list of problems that can happen, e.g.
:::info Example continued
Following the same problem.
- What if the sensor doesn't work? - What if the sensor doesn't work?
- What if the data doesn't conform to the specification? - What if the data doesn't conform to the specification?
- What if my formulas are wrong? - What if my formulas are wrong?
- anything important
:::
- Write down **anything** you consider important to solving the problem, e.g.
:::info Example continued once again
- I could probably use gyroscope. - I could probably use gyroscope.
- I should probably look up the datasheet for that module. - I should probably look up the datasheet for that module.
- I could write some tests to verify that my computations are correct. - I could write some tests to verify that my computations are correct.
@ -155,7 +144,9 @@ the ZIP-file, you can there:
- `skeleton.py` - skeleton for your solution, needs to be put in the same directory - `skeleton.py` - skeleton for your solution, needs to be put in the same directory
as `karel_tk.py` and takes path to the world as a first argument, example usage: as `karel_tk.py` and takes path to the world as a first argument, example usage:
```
$ python3 skeleton.py stairs.kw $ python3 skeleton.py stairs.kw
```
- of course, this file can be renamed ;) - of course, this file can be renamed ;)

View file

@ -0,0 +1,205 @@
---
slug: placeholders
title: Placeholders
description: |
Placeholders that are quite convenient to use when working on the code.
last_update:
date: 2023-11-24
---
Here we will try to implement some placeholders that you can find in other
languages, but I miss them in the C++. I'm taking the inspiration from languages
like Rust (all that we will implement) or Kotlin (`TODO`) that have them
implemented.
You may ask what placeholders do we need in the code, in our case we will be
talking about TODOs and unexpected situations, such as not implemented branches.
Namely we will implement
- `todo`,
- `unimplemented`, and
- `unreachable`.
## Design
If we take the two languages mentioned above as examples, there are at least two
ways how to implement them:
1. _panic_ when they are reached (as they do in Rust), or
2. _raise_ an exception when they are reached (as they do in Kotlin).
I will choose raising an exception, since the closest equivalent of _panic_ in
C++ would be `assert`s that are (by default) disabled in the _release builds_.
However I am too lazy to do:
```cpp
throw todo();
// or
throw todo("optional note");
```
Therefore we will implement exceptions and also wrap them in functions, so that
we can do:
```cpp
todo();
// or
todo("optional note");
```
:::tip
Wrapping them in a function (or macro) will allow us to do a little magic trick.
:::
## Implementation
We're going to utilize the exceptions, so we'll need to include the `exception`
header and we will start with a simple `_todo` exception class.
```cpp
#include <exception>
#include <string>
class _todo : public std::exception {
std::string cause;
public:
_todo() : cause("not yet implemented") {}
_todo(std::string&& excuse) : cause("not yet implemented: " + excuse) {}
virtual const char* what() const throw() { return cause.c_str(); }
};
```
In this case we have 2 constructors:
1. default constructor without any parameters that will return just
`not yet implemented`
2. and one parametrized with an “excuse” that will return string like:
`not yet implemented: excuse`
If we were to use it now, we would need to do something like:
```cpp
#include "placeholders.hpp"
int main() {
throw _todo();
return 0;
}
```
## Wrapping in a function
I am a lazy person, so we will wrap the exception in a function that will throw
it:
```cpp
void todo() {
throw _todo();
}
```
This can be used like:
```cpp
#include "placeholders.hpp"
int main() {
todo();
return 0;
}
```
## Magic trick
At the beginning I've mentioned that by wrapping the exceptions in a helper
functions that will throw them, we can do a nice magic trick :smile: This trick
will consist of formatted string and for that we will use
[`std::format`](https://en.cppreference.com/w/cpp/utility/format/format) that is
available since C++20.
We just need to add one more overload for our `todo()`:
```cpp
#include <format>
template< class... Args >
void todo(std::format_string<Args...> fmt, Args&&... args) {
throw _todo(std::format(fmt, args...));
}
```
## Finishing off with 2 more exceptions
Now we can repeat the same process for the other two exceptions I've mentioned
- `unimplemented`, and
- `unreachable`.
In the end we should end up with something like this:
```cpp
#include <exception>
#include <format>
#include <string>
class _todo : public std::exception {
std::string cause;
public:
_todo() : cause("not yet implemented") {}
_todo(std::string&& excuse) : cause("not yet implemented: " + excuse) {}
virtual const char* what() const throw() { return cause.c_str(); }
};
void todo() { throw _todo(); }
template <class... Args>
void todo(std::format_string<Args...> fmt, Args&&... args) {
throw _todo(std::format(fmt, args...));
}
class _unimplemented : public std::exception {
std::string cause;
public:
_unimplemented() : cause("not implemented") {}
_unimplemented(std::string&& excuse)
: cause("not implemented: " + excuse) {}
virtual const char* what() const throw() { return cause.c_str(); }
};
void unimplemented() { throw _unimplemented(); }
template <class... Args>
void unimplemented(std::format_string<Args...> fmt, Args&&... args) {
throw _unimplemented(std::format(fmt, args...));
}
class _unreachable : public std::exception {
std::string cause;
public:
_unreachable() : cause("entered unreachable code") {}
_unreachable(std::string&& excuse)
: cause("entered unreachable code: " + excuse) {}
virtual const char* what() const throw() { return cause.c_str(); }
};
void unreachable() { throw _unreachable(); }
template <class... Args>
void unreachable(std::format_string<Args...> fmt, Args&&... args) {
throw _unreachable(std::format(fmt, args...));
}
```
:::info
Final source code: [`placeholders.hpp`](pathname:///files/cpp/exceptions-and-raii/placeholders/placeholders.hpp)
:::

View file

@ -0,0 +1,5 @@
label: Exceptions and RAII
link:
type: generated-index
description: |
Materials related to the exceptions or RAII in C++.

View file

@ -0,0 +1,77 @@
#include <exception>
#include <format>
#include <string>
class _todo : public std::exception {
std::string cause;
public:
_todo() : cause("not yet implemented") {}
_todo(std::string&& excuse) : cause("not yet implemented: " + excuse) {}
virtual const char* what() const throw() { return cause.c_str(); }
};
/**
* @brief Indicates unfinished code.
*/
void todo() { throw _todo(); }
/**
* @brief Indicates unfinished code.
* @param fmt an object that represents the format string
* @param args arguments to be formatted
*/
template <class... Args>
void todo(std::format_string<Args...> fmt, Args&&... args) {
throw _todo(std::format(fmt, args...));
}
class _unimplemented : public std::exception {
std::string cause;
public:
_unimplemented() : cause("not implemented") {}
_unimplemented(std::string&& excuse)
: cause("not implemented: " + excuse) {}
virtual const char* what() const throw() { return cause.c_str(); }
};
/**
* @brief Indicates unimplemented code by throwing with a message of not implemented.
*/
void unimplemented() { throw _unimplemented(); }
/**
* @brief Indicates unimplemented code by throwing with a message of not implemented.
* @param fmt an object that represents the format string
* @param args arguments to be formatted
*/
template <class... Args>
void unimplemented(std::format_string<Args...> fmt, Args&&... args) {
throw _unimplemented(std::format(fmt, args...));
}
class _unreachable : public std::exception {
std::string cause;
public:
_unreachable() : cause("entered unreachable code") {}
_unreachable(std::string&& excuse)
: cause("entered unreachable code: " + excuse) {}
virtual const char* what() const throw() { return cause.c_str(); }
};
/**
* @brief Indicates unreachable code.
*/
void unreachable() { throw _unreachable(); }
/**
* @brief Indicates unreachable code.
* @param fmt an object that represents the format string
* @param args arguments to be formatted
*/
template <class... Args>
void unreachable(std::format_string<Args...> fmt, Args&&... args) {
throw _unreachable(std::format(fmt, args...));
}

View file

@ -0,0 +1,6 @@
#include "placeholders.hpp"
int main() {
todo("finish later ({})", "in 3 days");
return 0;
}