diff --git a/cpp/07-exceptions-and-raii/2023-11-24-placeholders.md b/cpp/07-exceptions-and-raii/2023-11-24-placeholders.md new file mode 100644 index 0000000..a7f4c15 --- /dev/null +++ b/cpp/07-exceptions-and-raii/2023-11-24-placeholders.md @@ -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 +#include + +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 + +template< class... Args > +void todo(std::format_string 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 +#include +#include + +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 +void todo(std::format_string 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 +void unimplemented(std::format_string 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 +void unreachable(std::format_string fmt, Args&&... args) { + throw _unreachable(std::format(fmt, args...)); +} +``` + +:::info + +Final source code: [`placeholders.hpp`](pathname:///files/cpp/exceptions-and-raii/placeholders/placeholders.hpp) + +::: diff --git a/cpp/07-exceptions-and-raii/_category_.yml b/cpp/07-exceptions-and-raii/_category_.yml new file mode 100644 index 0000000..11206ca --- /dev/null +++ b/cpp/07-exceptions-and-raii/_category_.yml @@ -0,0 +1,5 @@ +label: Exceptions and RAII +link: + type: generated-index + description: | + Materials related to the exceptions or RAII in C++. diff --git a/static/files/cpp/exceptions-and-raii/placeholders/placeholders.hpp b/static/files/cpp/exceptions-and-raii/placeholders/placeholders.hpp new file mode 100644 index 0000000..9ffbf8c --- /dev/null +++ b/static/files/cpp/exceptions-and-raii/placeholders/placeholders.hpp @@ -0,0 +1,77 @@ +#include +#include +#include + +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 +void todo(std::format_string 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 +void unimplemented(std::format_string 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 +void unreachable(std::format_string fmt, Args&&... args) { + throw _unreachable(std::format(fmt, args...)); +} diff --git a/static/files/cpp/exceptions-and-raii/placeholders/test.cpp b/static/files/cpp/exceptions-and-raii/placeholders/test.cpp new file mode 100644 index 0000000..695d09e --- /dev/null +++ b/static/files/cpp/exceptions-and-raii/placeholders/test.cpp @@ -0,0 +1,6 @@ +#include "placeholders.hpp" + +int main() { + todo("finish later ({})", "in 3 days"); + return 0; +}