This project has been created as part of the 42 curriculum by baelgadi.

CPP Module 05

Repetition and Exceptions


An uncaught throw doesn’t slow down. Rather, it tears through everything until something grabs it.
That’s the Speed Force for you; step in and you’re gone.

A catch block is the lightning rod, for without it, std::terminate() and you’re not coming back.


Previously…


CPP Module 05:

4 exercises. C++98 standard only.
Compiler: c++ · Flags: -Wall -Wextra -Werror · Must compile with -std=c++98

Exercise 00: Mommy, when I grow up, I want to be a bureaucrat! · ex00/ · Bureaucrat + nested exception classes
Exercise 01: Form up, maggots! · ex01/ · Form, beSigned, const members
Exercise 02: No, you need form 28B, not 28C · ex02/ · AForm becomes abstract + 3 concrete forms
Exercise 03: At least this beats coffee-making · ex03/ · Intern and makeForm

Forbidden: using namespace (-42) · friend (-42) · printf · *alloc · free · STL containers · STL algorithms · function bodies in headers
Required: Orthodox Canonical Form on every class · every exception derives from std::exception · what() is const throw() · a virtual destructor on AForm

Exceptions

In C, failure was merely a return value. -1, NULL, errno and a caller who might read it… or might not.
The error travelled at the same speed as everything else, and it could be ignored at every step.

An exception doesn’t travel like that.

if (_grade < 1)
    throw GradeTooHighException(); // the function does not return, it's gone

throw abandons the current function immediately, so no return value & no remaining statements.
The runtime then walks back up the call stack and while looking for a handler, it destroys every local object it passes on the way.

try
{
    Bureaucrat b("Wally", 0); // throws before b ever exists
    std::cout << b << std::endl; // never runs
}
catch (const std::exception &e) // the lightning rod
{
    std::cerr << "Exception: " << e.what() << std::endl;
}
- Return code (C) Exception (C++)
Can the caller ignore it? Yes, silently No, it keeps climbing
Works in a constructor? No, constructors return nothing Yes
Does it carry a message? Only via errno Yes, what()
Cost when nothing fails A comparison per call Nada

[!WARNING] If no catch on the stack matches, the search ends and std::terminate() is called.
No lightning rod, no way back.


Custom Exception Classes

You can throw anything: an int, a string, a Bureaucrat…

class Bureaucrat
{
    public:
        class GradeTooHighException : public std::exception
        {
            public:
                virtual const char *what() const throw();
        };

        class GradeTooLowException : public std::exception
        {
            public:
                virtual const char *what() const throw();
        };
};

Both classes are nested inside Bureaucrat and that’s deliberate.
AForm has exceptions with the exact same names and nesting keeps them apart:

Bureaucrat::GradeTooHighException // the one about a person
AForm::GradeTooHighException // the one about a document

The definition lives in the .cpp, like every other member function:

const char *Bureaucrat::GradeTooHighException::what() const throw()
{
    return "Grade is too high (minimum is 1)";
}

That signature has 3 parts and none of them are optional.

Part Why
const char * The base returns a C string, so the override must also
const The base’s what() is const
throw() In C++98 this is the way of saying “this never throws”

[!IMPORTANT] throw() is the C++98 spelling of noexcept (C++11) which is obviously not authorized here.

[!NOTE] Inheriting from std::exception means a single catch (const std::exception &e) handles every exception in the module, ours and the standard library’s alike.


Throwing from a Constructor

Barry ran into the Speed Force once.
There was no Barry left standing afterwards, and perhaps there never was a Barry at all.
A constructor that throws works exactly the same way.

Bureaucrat::Bureaucrat(const std::string &name, int grade)
    : _name(name), _grade(grade)
{
    if (_grade < 1)
        throw GradeTooHighException();
    if (_grade > 150)
        throw GradeTooLowException();
}

The validation is in the body, after the initialization list, since _name and _grade are set before the body runs.

Then the important part:

Bureaucrat b("Wally", 0); // throws
// ~Bureaucrat() is NEVER called (b never came into existence)

The object was never fully constructed so it is never destroyed.
What is cleaned up is every member that already finished constructing: _name is a fully built std::string by the time the body throws, so its destructor runs and its memory is freed.

Already constructed members The object itself
Destroyed on throw? Yes No
Who handles it The compiler Nobody

[!WARNING] If a constructor does _buf = new char[42]; and then throws, you’ve got yourself a memory leak. No destructor will ever run to free it.
CPP05 does not allocate in constructors, so this is safe here.


From Form to AForm

ex01 has a Form we can build. ex02 renames it AForm and makes it impossible to build.
Barry is not “a speedster” in the abstract. He is the Flash, or nothing at all.

class AForm
{
    public:
        void execute(const Bureaucrat &executor) const; // public, concrete

    protected:
        virtual void executeAction() const = 0; // pure virtual, abstract
};

execute() does every check, then hands off:

void AForm::execute(const Bureaucrat &executor) const
{
    if (!_signed)
        throw FormNotSignedException();
    if (executor.getGrade() > _gradeToExecute)
        throw GradeTooLowException();
    executeAction(); // only then does the derived class get a turn
}

This is the Template Method pattern.
The base owns the policy (must be signed / must be senior enough).
The derived class owns only the action.

That’s why a subclass cannot forget to check whether the form was signed. It never gets to make that decision.

executeAction() is protected, not public, so nothing outside the hierarchy can skip the checks by calling it directly.

[!NOTE] ex02 also adds a third exception FormNotSignedException. Ex01 has no execute(), so it has nothing to refuse yet.

[!WARNING] virtual ~AForm(); is mandatory. In ex03 the Intern hands us an AForm* that really points at a ShrubberyCreationForm, and we delete it through the base pointer.
(Without a virtual destructor that is undefined behavior, not just a leak)


The Three Forms

Three concrete forms, three different thresholds. Each one passes its own name and grades up to AForm and implements exactly one function.

Form Sign Execute executeAction()’s action
ShrubberyCreationForm 145 137 Create <target>_shrubbery in cwd with an ASCII tree
RobotomyRequestForm 72 45 Drilling noises, then succeed 50% of the time
PresidentialPardonForm 25 5 Announce the pardon by Zaphod Beeblebrox (hate that guy)
ShrubberyCreationForm::ShrubberyCreationForm(const std::string &target)
    : AForm("ShrubberyCreationForm", 145, 137), _target(target) {}
Bureaucrat b("Wally", 140);
ShrubberyCreationForm f("home");
b.signForm(f); // 140 <= 145 → signed
b.executeForm(f); // 140 > 137 → refused

The shrubbery writes to a real file, so it is the only form that could fail for a reason that has nothing to do with grades:

std::ofstream os(filename.c_str());
if (!os.is_open())
    throw std::runtime_error("cannot open file " + filename);

[!NOTE] The RobotomyRequestForm seeds std::srand once, guarded by a static bool to know if it has been seeded.
Seeding on every call with std::time(NULL) would give the same “random” answer for every execution inside the same second.
PRNG vs TRNG ;-)


The Intern & the Factory

The Intern has no name, no grade and no state. We don’t care who they are and we only care that we can hand them a string & get a form back.

AForm *Intern::makeForm(const std::string &formName, const std::string &target) const;

The naive version is an if/else chain, 3 strcmps deep, but the subject forbids it.
The version in ex03/Intern.cpp is a lookup table instead:

struct FormEntry
{
    const char  *name;
    AForm       *(*create)(const std::string &); // ptr to a function
};

static const FormEntry forms[] = {
    { "shrubbery creation", makeShrubbery },
    { "robotomy request",   makeRobotomy  },
    { "presidential pardon", makePresidential }
};

for (int i = 0; i < 3; i++)
{
    if (formName == forms[i].name)
        return forms[i].create(target);
}
return NULL;

makeForm does new. It doesn’t delete.
The caller does:

AForm *form = someRandomIntern.makeForm("robotomy request", "Victor Stone");
if (form)
{
    b.signForm(*form);
    b.executeForm(*form);
    delete form;
}

[!WARNING] An unknown form name returns NULL.
delete NULL; is safe and does nothing but b.signForm(*form) on a null pointer is a segfault.


Instructions

# ──── ex00 ────
# Verify: grade 0 throws GradeTooHigh, grade 151 throws GradeTooLow
# Verify: incrementing a grade - 1 bureaucrat throws and decrementing a grade - 150 one throws
# Verify: operator<< prints "<name>, bureaucrat grade <grade>."
cd ex00 && make
./bureaucrat
valgrind --leak-check=full ./bureaucrat

# ──── ex01 ────
# Verify: a form built with grade 0 or 151 throws at construction
# Verify: a bureaucrat with a grade that's too low fails to sign (and the form stays unsigned)
# Verify: signing an already signed form is harmless no-op
cd ex01 && make
./form
valgrind --leak-check=full ./form

# ──── ex02 ────
# Verify: 'AForm f("x", 1, 1);' fails to compile (abstract)
# Verify: executing an unsigned form throws FormNotSignedException
# Verify: the shrubbery file actually appears on disk and has ASCII trees
cd ex02 && make
./aform
cat home_shrubbery
valgrind --leak-check=full ./aform

# ──── ex03 ────
# Verify: all 3 known form names produce the right form
# Verify: an unknown name returns NULL
cd ex03 && make
./intern
valgrind --leak-check=full ./intern

Resources


You can run the whole module in an afternoon but speed isn’t what is being graded here.
What is graded is whether every throw has a lightning rod waiting for it.

Hey, the module went down, and nobody got hurt. You know what I call that? A really good day.