cpp

Exceptions

02_Exceptions⚙️
/**
 * ============================================================
 * C++ EXCEPTION HANDLING
 * ============================================================
 * 
 * This file covers:
 * - try, catch, throw basics
 * - Standard exceptions
 * - Custom exception classes
 * - Exception specifications
 * - RAII and exceptions
 * - Best practices
 * 
 * Compile: g++ -std=c++17 -Wall 01_exceptions.cpp -o exceptions
 * Run: ./exceptions
 * 
 * ============================================================
 */

#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
#include <memory>
#include <fstream>

using namespace std;

// ============================================================
// PART 1: BASIC EXCEPTION HANDLING
// ============================================================

double divide(double a, double b) {
    if (b == 0) {
        throw runtime_error("Division by zero!");
    }
    return a / b;
}

int getElement(const vector<int>& vec, int index) {
    if (index < 0 || index >= static_cast<int>(vec.size())) {
        throw out_of_range("Index " + to_string(index) + " is out of range");
    }
    return vec[index];
}

// ============================================================
// PART 2: CUSTOM EXCEPTION CLASSES
// ============================================================

// Simple custom exception
class FileException : public exception {
private:
    string message;
    
public:
    FileException(const string& msg) : message(msg) {}
    
    const char* what() const noexcept override {
        return message.c_str();
    }
};

// Exception with additional data
class ValidationException : public exception {
private:
    string field;
    string value;
    string message;
    mutable string fullMessage;
    
public:
    ValidationException(const string& f, const string& v, const string& msg)
        : field(f), value(v), message(msg) {}
    
    const char* what() const noexcept override {
        fullMessage = "Validation error in '" + field + "': " + message + 
                      " (value: '" + value + "')";
        return fullMessage.c_str();
    }
    
    const string& getField() const { return field; }
    const string& getValue() const { return value; }
};

// Exception hierarchy
class DatabaseException : public runtime_error {
public:
    DatabaseException(const string& msg) : runtime_error(msg) {}
};

class ConnectionException : public DatabaseException {
public:
    ConnectionException(const string& host)
        : DatabaseException("Could not connect to: " + host) {}
};

class QueryException : public DatabaseException {
private:
    string query;
    
public:
    QueryException(const string& q, const string& error)
        : DatabaseException("Query failed: " + error), query(q) {}
    
    const string& getQuery() const { return query; }
};

// ============================================================
// PART 3: FUNCTIONS THAT THROW
// ============================================================

void processFile(const string& filename) {
    ifstream file(filename);
    if (!file) {
        throw FileException("Could not open file: " + filename);
    }
    
    string line;
    int lineNum = 0;
    while (getline(file, line)) {
        lineNum++;
        if (line.empty()) {
            throw runtime_error("Empty line at line " + to_string(lineNum));
        }
    }
}

void validateEmail(const string& email) {
    if (email.empty()) {
        throw ValidationException("email", email, "cannot be empty");
    }
    if (email.find('@') == string::npos) {
        throw ValidationException("email", email, "must contain @");
    }
    if (email.find('.') == string::npos) {
        throw ValidationException("email", email, "must contain .");
    }
}

void connectToDatabase(const string& host, bool simulateFail = true) {
    if (simulateFail) {
        throw ConnectionException(host);
    }
}

// ============================================================
// PART 4: RAII AND EXCEPTIONS
// ============================================================

class Resource {
private:
    string name;
    
public:
    Resource(const string& n) : name(n) {
        cout << "  Resource '" << name << "' acquired" << endl;
    }
    
    ~Resource() {
        cout << "  Resource '" << name << "' released" << endl;
    }
    
    void use() {
        cout << "  Using resource '" << name << "'" << endl;
    }
};

// Smart pointer for exception-safe resource management
class SafeOperation {
private:
    unique_ptr<Resource> resource;
    
public:
    SafeOperation(const string& name) 
        : resource(make_unique<Resource>(name)) {}
    
    void operate(bool shouldThrow = false) {
        resource->use();
        if (shouldThrow) {
            throw runtime_error("Operation failed!");
        }
    }
    // Resource automatically cleaned up even if exception thrown
};

// ============================================================
// MAIN FUNCTION
// ============================================================

int main() {
    cout << "============================================" << endl;
    cout << "     C++ EXCEPTION HANDLING" << endl;
    cout << "============================================" << endl << endl;

    // ========================================================
    // DEMO 1: Basic try-catch
    // ========================================================
    
    cout << "--- DEMO 1: BASIC TRY-CATCH ---" << endl << endl;
    
    // Successful operation
    try {
        double result = divide(10, 2);
        cout << "10 / 2 = " << result << endl;
    } catch (const runtime_error& e) {
        cout << "Error: " << e.what() << endl;
    }
    
    // Exception thrown
    try {
        double result = divide(10, 0);
        cout << "10 / 0 = " << result << endl;  // Never reached
    } catch (const runtime_error& e) {
        cout << "Error: " << e.what() << endl;
    }
    
    cout << "\nProgram continues after exception handled!" << endl;
    
    cout << endl;

    // ========================================================
    // DEMO 2: Multiple catch blocks
    // ========================================================
    
    cout << "--- DEMO 2: MULTIPLE CATCH BLOCKS ---" << endl << endl;
    
    vector<int> vec = {1, 2, 3, 4, 5};
    
    auto tryAccess = [&vec](int index) {
        try {
            int value = getElement(vec, index);
            cout << "vec[" << index << "] = " << value << endl;
        } catch (const out_of_range& e) {
            cout << "Out of range: " << e.what() << endl;
        } catch (const exception& e) {
            cout << "Exception: " << e.what() << endl;
        } catch (...) {  // Catch all
            cout << "Unknown exception!" << endl;
        }
    };
    
    tryAccess(2);
    tryAccess(10);
    tryAccess(-1);
    
    cout << endl;

    // ========================================================
    // DEMO 3: Standard Exceptions
    // ========================================================
    
    cout << "--- DEMO 3: STANDARD EXCEPTIONS ---" << endl << endl;
    
    cout << "Standard exception hierarchy:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << "exception" << endl;
    cout << "├── logic_error" << endl;
    cout << "│   ├── invalid_argument" << endl;
    cout << "│   ├── domain_error" << endl;
    cout << "│   ├── length_error" << endl;
    cout << "│   └── out_of_range" << endl;
    cout << "├── runtime_error" << endl;
    cout << "│   ├── range_error" << endl;
    cout << "│   ├── overflow_error" << endl;
    cout << "│   └── underflow_error" << endl;
    cout << "└── bad_alloc, bad_cast, etc." << endl;
    
    // Throwing standard exceptions
    try {
        throw invalid_argument("Invalid argument provided");
    } catch (const invalid_argument& e) {
        cout << "\nCaught invalid_argument: " << e.what() << endl;
    }
    
    try {
        throw length_error("Length exceeds maximum");
    } catch (const length_error& e) {
        cout << "Caught length_error: " << e.what() << endl;
    }
    
    cout << endl;

    // ========================================================
    // DEMO 4: Custom Exception Classes
    // ========================================================
    
    cout << "--- DEMO 4: CUSTOM EXCEPTIONS ---" << endl << endl;
    
    // Simple custom exception
    try {
        throw FileException("config.ini not found");
    } catch (const FileException& e) {
        cout << "FileException: " << e.what() << endl;
    }
    
    // Exception with data
    try {
        validateEmail("invalid-email");
    } catch (const ValidationException& e) {
        cout << "\nValidationException:" << endl;
        cout << "  " << e.what() << endl;
        cout << "  Field: " << e.getField() << endl;
        cout << "  Value: " << e.getValue() << endl;
    }
    
    // Exception hierarchy
    try {
        connectToDatabase("localhost:5432");
    } catch (const ConnectionException& e) {
        cout << "\nConnectionException: " << e.what() << endl;
    } catch (const DatabaseException& e) {
        cout << "\nDatabaseException: " << e.what() << endl;
    }
    
    cout << endl;

    // ========================================================
    // DEMO 5: Rethrowing Exceptions
    // ========================================================
    
    cout << "--- DEMO 5: RETHROWING ---" << endl << endl;
    
    auto middleFunction = []() {
        try {
            divide(1, 0);
        } catch (const exception& e) {
            cout << "Middle: Logging error: " << e.what() << endl;
            throw;  // Rethrow same exception
        }
    };
    
    try {
        middleFunction();
    } catch (const exception& e) {
        cout << "Outer: Handling error: " << e.what() << endl;
    }
    
    // Wrapping exception
    auto wrapException = []() {
        try {
            throw runtime_error("Original error");
        } catch (const runtime_error& e) {
            throw DatabaseException("Wrapped: " + string(e.what()));
        }
    };
    
    try {
        wrapException();
    } catch (const DatabaseException& e) {
        cout << "\nWrapped exception: " << e.what() << endl;
    }
    
    cout << endl;

    // ========================================================
    // DEMO 6: RAII and Exception Safety
    // ========================================================
    
    cout << "--- DEMO 6: RAII & EXCEPTION SAFETY ---" << endl << endl;
    
    cout << "Without exception:" << endl;
    {
        SafeOperation op("resource1");
        op.operate(false);  // No throw
    }
    
    cout << "\nWith exception:" << endl;
    try {
        SafeOperation op("resource2");
        op.operate(true);  // Throws
    } catch (const exception& e) {
        cout << "  Caught: " << e.what() << endl;
    }
    // Resource still properly released!
    
    cout << "\n💡 Resources released even when exception thrown!" << endl;
    
    cout << endl;

    // ========================================================
    // DEMO 7: noexcept Specification
    // ========================================================
    
    cout << "--- DEMO 7: NOEXCEPT ---" << endl << endl;
    
    // noexcept means function won't throw
    auto safeAdd = [](int a, int b) noexcept {
        return a + b;
    };
    
    // noexcept(condition) - conditional
    auto conditionalNoexcept = [](int x) noexcept(sizeof(int) == 4) {
        return x * 2;
    };
    
    cout << "noexcept(safeAdd): " << noexcept(safeAdd(1, 2)) << endl;
    cout << "noexcept(divide): " << noexcept(divide(1, 2)) << endl;
    
    cout << "\nnoexcept benefits:" << endl;
    cout << "• Enables optimizations" << endl;
    cout << "• Required for move operations in containers" << endl;
    cout << "• Documents function behavior" << endl;
    
    cout << endl;

    // ========================================================
    // DEMO 8: Exception in Constructors/Destructors
    // ========================================================
    
    cout << "--- DEMO 8: EXCEPTIONS IN CTORS ---" << endl << endl;
    
    class ThrowingClass {
    private:
        unique_ptr<Resource> r1;
        unique_ptr<Resource> r2;
        
    public:
        ThrowingClass(bool throwInMiddle = false) {
            r1 = make_unique<Resource>("first");
            
            if (throwInMiddle) {
                throw runtime_error("Construction failed!");
            }
            
            r2 = make_unique<Resource>("second");
        }
    };
    
    cout << "Successful construction:" << endl;
    {
        ThrowingClass obj(false);
    }
    
    cout << "\nFailed construction:" << endl;
    try {
        ThrowingClass obj(true);
    } catch (const exception& e) {
        cout << "  Caught: " << e.what() << endl;
    }
    // First resource properly cleaned up!
    
    cout << endl;

    // ========================================================
    // EXCEPTION BEST PRACTICES
    // ========================================================
    
    cout << "--- EXCEPTION BEST PRACTICES ---" << endl << endl;
    
    cout << "DO:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << "✓ Throw by value, catch by const reference" << endl;
    cout << "✓ Use standard exceptions when appropriate" << endl;
    cout << "✓ Make destructors noexcept (implicit)" << endl;
    cout << "✓ Use RAII for resource management" << endl;
    cout << "✓ Catch at appropriate level" << endl;
    cout << "✓ Provide informative error messages" << endl;
    
    cout << "\nDON'T:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << "✗ Throw from destructors" << endl;
    cout << "✗ Catch exceptions you can't handle" << endl;
    cout << "✗ Use exceptions for normal flow control" << endl;
    cout << "✗ Throw pointers (memory management issues)" << endl;
    cout << "✗ Use catch(...) without rethrowing" << endl;
    
    cout << endl;

    cout << "============================================" << endl;
    cout << "EXCEPTION HANDLING COMPLETE!" << endl;
    cout << "============================================" << endl;

    return 0;
}

// ============================================================
// EXERCISES:
// ============================================================
/*
 * 1. Create a bank account class:
 *    - InsufficientFundsException
 *    - InvalidAmountException
 *    - AccountClosedException
 *    - Implement withdraw, deposit, transfer
 * 
 * 2. Create a parser with custom exceptions:
 *    - SyntaxException with line number
 *    - SemanticException with context
 *    - Parse a simple config file format
 * 
 * 3. Create exception-safe container:
 *    - SafeVector with bounds checking
 *    - Strong exception guarantee for push_back
 *    - Use smart pointers internally
 * 
 * 4. Implement exception logging:
 *    - Log exception details to file
 *    - Include timestamp, type, message
 *    - Support for nested exceptions
 */
Exceptions - C++ Tutorial | DeepML