Programming Language & Its Applications β Generic Programming and Exception Handling, NEC licence examination syllabus (Nepal Engineering Council).
Several catch blocks on one try β and why their order decides which one runs.
-Woverloaded-virtual family includes a warning for exactly this mistake.#include <iostream>
#include <stdexcept>
#include <string>
void thrower(int which) {
switch (which) {
case 1: throw std::out_of_range("index 99 is out of range");
case 2: throw std::invalid_argument("age cannot be negative");
case 3: throw std::runtime_error("the network is unreachable");
case 4: throw std::bad_alloc();
case 5: throw 42;
case 6: throw std::string("a bare string");
default: std::cout << " no throw\n";
}
}
/* CORRECT ordering: derived -> base -> catch-all */
void handleWell(int which) {
std::cout << " case " << which << ": ";
try { thrower(which); }
catch (const std::out_of_range &e) {
std::cout << "[out_of_range] " << e.what() << "\n";
}
catch (const std::logic_error &e) {
std::cout << "[logic_error] " << e.what() << "\n";
}
catch (const std::runtime_error &e) {
std::cout << "[runtime_error] " << e.what() << "\n";
}
catch (const std::exception &e) {
std::cout << "[std::exception] " << e.what() << "\n";
}
catch (int n) {
std::cout << "[int] " << n << "\n";
}
catch (...) {
std::cout << "[catch-all] unknown type\n";
}
}
/* WRONG ordering: the base swallows everything */
void handleBadly(int which) {
std::cout << " case " << which << ": ";
try { thrower(which); }
catch (const std::exception &e) {
std::cout << "[exception FIRST] " << e.what() << "\n";
}
catch (const std::out_of_range &e) {
std::cout << "[NEVER REACHED]\n";
}
catch (...) {
std::cout << "[catch-all] non-std type\n";
}
}
int main() {
std::cout << " CORRECT ordering:\n";
for (int i = 1; i <= 7; i++) handleWell(i);
std::cout << " WRONG ordering (base first):\n";
for (int i = 1; i <= 3; i++) handleBadly(i);
handleBadly(6);
return 0;
}
Output:
CORRECT ordering:
case 1: [out_of_range] index 99 is out of range
case 2: [logic_error] age cannot be negative
case 3: [runtime_error] the network is unreachable
case 4: [std::exception] std::bad_alloc
case 5: [int] 42
case 6: [catch-all] unknown type
case 7: no throw
WRONG ordering (base first):
case 1: [exception FIRST] index 99 is out of range
case 2: [exception FIRST] age cannot be negative
case 3: [exception FIRST] the network is unreachable
case 6: [catch-all] non-std type
bad_alloc fell through to catch(const std::exception&) rather than logic_error or runtime_error. It derives directly from std::exception, as do bad_cast and bad_typeid. So a handler catching only logic_error and runtime_error misses allocation failures entirely β a real gap in code that thinks it has covered everything.
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
#include <sstream>
/* Custom exception types, each carrying its own data β
because different failures need different information */
class ParseError : public std::runtime_error {
int lineNo;
public:
ParseError(const std::string &msg, int line)
: std::runtime_error(msg), lineNo(line) {}
int line() const { return lineNo; }
};
class RangeError : public std::out_of_range {
double value, lo, hi;
public:
RangeError(double v, double a, double b)
: std::out_of_range("value out of range"),
value(v), lo(a), hi(b) {}
double got() const { return value; }
double min() const { return lo; }
double max() const { return hi; }
};
class MissingField : public std::invalid_argument {
std::string field;
public:
MissingField(const std::string &f)
: std::invalid_argument("missing field: " + f), field(f) {}
const std::string& name() const { return field; }
};
/* parse "roll,name,marks" with full validation */
struct Record { int roll; std::string name; double marks; };
Record parseLine(const std::string &line, int lineNo) {
std::istringstream is(line);
std::string rollStr, name, marksStr;
if (!std::getline(is, rollStr, ',')) throw MissingField("roll");
if (!std::getline(is, name, ',')) throw MissingField("name");
if (!std::getline(is, marksStr)) throw MissingField("marks");
Record r;
try {
r.roll = std::stoi(rollStr);
} catch (const std::exception&) {
throw ParseError("roll is not an integer: " + rollStr, lineNo);
}
try {
r.marks = std::stod(marksStr);
} catch (const std::exception&) {
throw ParseError("marks is not a number: " + marksStr, lineNo);
}
if (name.empty()) throw MissingField("name");
if (r.marks < 0 || r.marks > 100)
throw RangeError(r.marks, 0, 100);
r.name = name;
return r;
}
int main() {
std::vector<std::string> lines = {
"101,Ram Bahadur,87.5", /* valid */
"102,Sita Devi,150", /* marks out of range */
"abc,Hari Prasad,76", /* roll not a number */
"104,,68", /* empty name */
"105,Gita", /* missing marks */
"106,Bikash,ninety" /* marks not a number */
};
int ok = 0, failed = 0;
for (size_t i = 0; i < lines.size(); i++) {
std::cout << " line " << (i+1) << ": ";
try {
Record r = parseLine(lines[i], int(i+1));
std::cout << "OK " << r.roll << " " << r.name
<< " " << r.marks << "\n";
++ok;
}
/* most specific first, each using its OWN data */
catch (const RangeError &e) {
std::cout << "RANGE got " << e.got()
<< ", allowed " << e.min() << ".."
<< e.max() << "\n";
++failed;
}
catch (const MissingField &e) {
std::cout << "FIELD '" << e.name()
<< "' is required\n";
++failed;
}
catch (const ParseError &e) {
std::cout << "PARSE line " << e.line()
<< ": " << e.what() << "\n";
++failed;
}
catch (const std::exception &e) {
std::cout << "OTHER " << e.what() << "\n";
++failed;
}
}
std::cout << " --- " << ok << " accepted, "
<< failed << " rejected ---\n";
return 0;
}
Output:
line 1: OK 101 Ram Bahadur 87.5
line 2: RANGE got 150, allowed 0..100
line 3: PARSE line 3: roll is not an integer: abc
line 4: FIELD 'name' is required
line 5: FIELD 'marks' is required
line 6: PARSE line 6: marks is not a number: ninety
--- 1 accepted, 5 rejected ---
#include <iostream>
#include <stdexcept>
#include <vector>
#include <string>
/* An inner try handles what it can and lets the rest
propagate outward β the normal layered design. */
double processOne(const std::string &s) {
if (s == "zero") throw std::domain_error("division by zero");
if (s == "fatal") throw std::runtime_error("unrecoverable");
if (s == "junk") throw std::invalid_argument("not a number");
return std::stod(s) * 2;
}
int main() {
std::vector<std::string> inputs =
{"21", "junk", "zero", "7.5", "fatal", "3"};
double total = 0;
int skipped = 0;
try {
for (const auto &s : inputs) {
/* INNER try: recover from per-item problems */
try {
double v = processOne(s);
total += v;
std::cout << " " << s << " -> " << v << "\n";
}
catch (const std::invalid_argument &e) {
std::cout << " " << s << " -> skipped ("
<< e.what() << ")\n";
++skipped;
}
catch (const std::domain_error &e) {
std::cout << " " << s << " -> skipped ("
<< e.what() << ")\n";
++skipped;
}
/* runtime_error is NOT caught here β it
propagates to the OUTER try */
}
std::cout << " loop completed\n";
}
catch (const std::runtime_error &e) {
std::cout << " ABORTED by: " << e.what() << "\n";
}
std::cout << " total=" << total << " skipped=" << skipped
<< "\n";
return 0;
}
Output:
21 -> 42
junk -> skipped (not a number)
zero -> skipped (division by zero)
7.5 -> 15
ABORTED by: unrecoverable
total=57 skipped=2
std::nested_exception and std::throw_with_nested, which let a handler catch a low-level error, wrap it in a higher-level one, and preserve the whole chain β so a "cannot load config" error still carries the underlying "file not found" inside it. Java has had this as getCause() since 1.4 and it is invaluable for debugging layered systems. Search "std::throw_with_nested rethrow_if_nested"; the printing helper is a short recursive function worth writing once.catch(...) last. Be ready to trace which handler catches each of several throws β that is the standard question. Know that a handler matches the exact type or any public base class, with no implicit conversions. Mention that bad_alloc derives directly from std::exception, and that a base-first ordering produces unreachable handlers that the compiler only warns about.Create a free account to tick topics off, take notes as you read, watch the video lessons and get a day-by-day study plan built around your exam date.
Loadingβ¦