mirror of
https://github.com/mfocko/blog.git
synced 2024-11-24 05:51:55 +01:00
Fix admonitions in Karel and add C++ placeholders (#4)
This commit is contained in:
commit
c06941d75a
5 changed files with 305 additions and 21 deletions
|
@ -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
|
||||
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
|
||||
- Read data from some sensor
|
||||
- From what sensor
|
||||
|
@ -92,30 +95,16 @@ your mind. Ideally:
|
|||
- How do I communicate with the sensor?
|
||||
- What is the meaning of the data that I got?
|
||||
- How can I process it?
|
||||
|
||||
:::
|
||||
|
||||
- Write down list of problems that can happen, e.g.
|
||||
|
||||
:::info Example continued
|
||||
|
||||
Following the same problem.
|
||||
|
||||
- any problems that can happen
|
||||
- What if the sensor doesn't work?
|
||||
- What if the data doesn't conform to the specification?
|
||||
- What if my formulas are wrong?
|
||||
|
||||
:::
|
||||
|
||||
- Write down **anything** you consider important to solving the problem, e.g.
|
||||
|
||||
:::info Example continued once again
|
||||
|
||||
- anything important
|
||||
- I could probably use gyroscope.
|
||||
- I should probably look up the datasheet for that module.
|
||||
- I could write some tests to verify that my computations are correct.
|
||||
|
||||
:::
|
||||
:::
|
||||
|
||||
## »Rough« pseudocode
|
||||
|
||||
|
@ -155,7 +144,9 @@ the ZIP-file, you can there:
|
|||
- `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:
|
||||
|
||||
$ python3 skeleton.py stairs.kw
|
||||
```
|
||||
$ python3 skeleton.py stairs.kw
|
||||
```
|
||||
|
||||
- of course, this file can be renamed ;)
|
||||
|
||||
|
|
205
cpp/07-exceptions-and-raii/2023-11-24-placeholders.md
Normal file
205
cpp/07-exceptions-and-raii/2023-11-24-placeholders.md
Normal 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)
|
||||
|
||||
:::
|
5
cpp/07-exceptions-and-raii/_category_.yml
Normal file
5
cpp/07-exceptions-and-raii/_category_.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
label: Exceptions and RAII
|
||||
link:
|
||||
type: generated-index
|
||||
description: |
|
||||
Materials related to the exceptions or RAII in C++.
|
|
@ -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...));
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#include "placeholders.hpp"
|
||||
|
||||
int main() {
|
||||
todo("finish later ({})", "in 3 days");
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue