Video Coming Soon...
52: Exceptions
Cover exceptions more.
The Code
View Source file ex52.cpp Only
#include <fmt/core.h>
#include <chrono>
#include <thread>
#include <string>
void thrower() {
throw std::runtime_error("Kaboom!");
}
void weird_catcher() try {
auto res = std::string("abc").substr(10);
} catch(const std::exception& e) {
fmt::println("weird error: {}", e.what());
}
struct Bomber {
std::string subst;
Bomber(const std::string& test) try : subst(test.substr(10)) {
fmt::println("It worked!");
} catch(const std::exception& e) {
fmt::println("Bomber failed {}", e.what());
}
};
void cant_throw() noexcept {
thrower();
}
int main(int argc, char* argv[]) {
weird_catcher();
try {
thrower();
} catch(const std::exception& e) {
fmt::println("error! {}", e.what());
}
Bomber test{"thishasenoughchars"};
try {
Bomber kaboom{"not"};
} catch(...) {
fmt::println("Bomber go boom!");
}
fmt::println("Calling cant_throw()");
cant_throw();
}
The Breakdown
line of code- Description.
The Discussion
Blah blah.
Safe Exceptions
- Always throw
std::exceptionor a derivative. - Always catch
const std::exception&. - Always use exceptions for exceptional things. Something that's a catastrophic abnormal situation, not for common conditions. For example, a user entering a number wrong is not "exceptional." Users enter numbers wrong all the time. Not being able to read the keyboard is exceptional.
- Don't use exceptions in tight loops or places where the cost of catching an exception is high. However, don't make this determination without measurements (as I've always said).
Problems with Exceptions
- Difficult to debug because C++ doesn't do stack traces, you neeed a debugger. Still, a few
gdbtricks fixes this. - When an exception is thrown it can be from anywhere in a whole
try/catch, but that's also kind of the point. Exceptions are for exceptional unexpected errors, so if you expect a possible error then you'd just handle it there.
Not Problems with Exceptions
- They're slow. No, turn on the optimizer and always base these claims on real measurements not "lore."
- They make binaries larger. True, but unless you're an embedded programmer you don't have to care about that.
- They cause memory problems. Only if you're doing dumb shit like ....
- Talk about this example being dumb because you should evaluate the input before the loop as a pre-condition or invariant.
- Exceptions have zero overhead when there are no errors when compared to other strategies that use return values.
struct invalid_value {};
void do_sqrt(std::span<double> values) {
for (auto& v : values) {
if (v < 0) throw invalid_value{};
v = std::sqrt(v);
}
}
From https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2544r0.html
Exception Weirdness?
Find out why this was created:
https://en.cppreference.com/cpp/language/using_declaration#:~:text=Inheriting%20constructors
The code that causes the problem is:
class MyException : public std::runtime_error {}
This doesn't inherit the constructors, which to me seems crazy wrong for an OOP language, so why was that done initially, and then why the weird solution of this:
class MyException : public std::runtime_error {
using std::runtime_error::runtime_error;
}
I must figure out the rationale behind why they thought not automatically inheriting the constructors was correct, and then why they back out of the decision with this weird specific use of using.
Further Study
- Do this next.
Register for Learn C++ the Hard Way
Register to gain access to additional videos which demonstrate each exercise. Videos are priced to cover the cost of hosting.