We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← RAII: Objects With Lifetimes step 4 of 4
Exception safety you did not have to write
Here is a function that is correct until someone else edits it:
void process(Store& store) {
store.lock();
auto data = load(); // <- adds a throw next year
store.write(data);
store.unlock();
}
The day load() gains a throw, the lock is never released, and the bug
reports say “the service hangs sometimes”. Nothing about process looks
wrong; the person who broke it never opened this file.
Two ways to fix it
The one most languages give you:
store.lock();
try {
auto data = load();
store.write(data);
} catch (...) {
store.unlock();
throw;
}
store.unlock();
Correct, and awful. The cleanup is written twice, the throw; rethrow is easy
to forget, and every future exit path has to remember all of it.
The one C++ gives you:
{
StoreLock lock{store}; // locks
auto data = load();
store.write(data);
} // unlocks — on every path there is
No try. No catch. No duplication. The cleanup is stated once, in the
type, and applies to paths that do not exist yet.
The guarantees, briefly
When people say a function is “exception safe” they mean one of:
| Guarantee | Means |
|---|---|
| Nothrow |
It will not throw at all. Destructors and swap must be this. |
| Strong | If it throws, nothing changed. All or nothing. |
| Basic | If it throws, everything is still valid and destructible — no leaks, no corruption — but the state may have changed. |
| (none) | It might leak or corrupt. |
RAII gives you the basic guarantee essentially for free: nothing leaks, because everything that was acquired is owned by something whose destructor runs. Getting from basic to strong takes deliberate work — usually building the result to one side and swapping it in at the end, which you will meet in Track 3 as copy-and-swap.
Your task
int apply_all(Ledger& ledger, const std::vector<int>& deltas);
For each delta, open a transaction on the ledger, add the delta, and close it.
A delta of exactly 13 throws std::runtime_error after the transaction has
been opened — that is the hostile edit, simulated.
Catch the exception in apply_all, stop processing, and return the balance.
Every transaction that was opened must be closed, including the one that
was open when the throw happened.
The starter opens and closes by hand and gets it wrong on exactly that path. Write a guard instead — the same shape as the previous problem — and the path stops existing.
Stuck?
C++ reference solution
Sign in to attempt this problem and reveal the reference solution.