Exercises: Exception Handling

Part A: Conceptual Questions

A1. Explain the difference between try..except and try..finally. When would you use each? When would you use both together?

A2. Why is "swallowing exceptions" (catching and ignoring them) considered a dangerous antipattern? Give a concrete scenario where this could cause real-world harm.

A3. Explain why catching Exception (the base class) is usually too broad. In what situation might catching the base Exception class be appropriate?

A4. Why should exceptions not be used for normal flow control? What is the performance consideration, and what is the readability consideration?

A5. Compare the IOResult approach to error handling with the exception-based approach. List three advantages of exceptions over IOResult.

A6. Explain the "write-to-temp-then-rename" pattern for safe file saving. Why is it safer than writing directly to the original file?

A7. Why is it important to order on clauses from most specific to most general? What happens if you put a generic Exception handler before a specific EConvertError handler?


Part B: Tracing and Prediction

B1. What is the output of the following code?

procedure Inner;
begin
  WriteLn('Inner: before raise');
  raise Exception.Create('Boom!');
  WriteLn('Inner: after raise');  { Does this execute? }
end;

procedure Middle;
begin
  WriteLn('Middle: before Inner');
  Inner;
  WriteLn('Middle: after Inner');  { Does this execute? }
end;

begin
  WriteLn('Main: before try');
  try
    Middle;
  except
    on E: Exception do
      WriteLn('Caught: ', E.Message);
  end;
  WriteLn('Main: after try');
end.

B2. What is the output of this code?

begin
  try
    WriteLn('A');
    try
      WriteLn('B');
      raise Exception.Create('Error');
      WriteLn('C');
    finally
      WriteLn('D');
    end;
    WriteLn('E');
  except
    on E: Exception do
      WriteLn('F: ', E.Message);
  end;
  WriteLn('G');
end.

B3. What is the output of this code? (Trace carefully — there are two nested try blocks.)

procedure ProcessItem(N: Integer);
begin
  if N = 0 then
    raise EDivByZero.Create('Division by zero');
  WriteLn(100 div N);
end;

var
  i: Integer;
begin
  for i := 3 downto -1 do
  begin
    try
      Write('Processing ', i, ': ');
      ProcessItem(i);
    except
      on E: EDivByZero do
        WriteLn('Skipped — ', E.Message);
      on E: Exception do
        WriteLn('Unknown error: ', E.Message);
    end;
  end;
  WriteLn('Done.');
end.

Part C: Short Programming Exercises

C1. Safe Division Function Write a function SafeDiv(A, B: Integer): Integer that returns A div B, or returns 0 and prints a warning if B is zero. Use exception handling, not an if check.

C2. Robust String-to-Integer Array Write a function ParseIntArray(const Input: String): TIntArray (where TIntArray = array of Integer) that takes a comma-separated string of numbers like '10,20,abc,30,xyz,40' and returns an array of successfully parsed integers, printing a warning for each invalid value. Use exceptions to detect invalid values.

C3. File Copy with Error Handling Write a procedure SafeFileCopy(const Source, Dest: String) that copies a text file line by line. Handle the following cases with appropriate exceptions: - Source file does not exist - Destination directory does not exist - Read error during copy Ensure all files are properly closed using try..finally.

C4. Input Validation Loop Write a procedure ReadPositiveFloat(const Prompt: String; out Value: Double) that repeatedly prompts the user until they enter a valid positive floating-point number. Handle EConvertError for non-numeric input and raise a custom ENegativeValueError for negative numbers.

C5. Custom Exception Hierarchy Design and implement a custom exception hierarchy for a library system: - ELibraryError (base) - EBookNotFoundError (includes book title) - EBookCheckedOutError (includes book title and borrower name) - EMaxBooksExceededError (includes member name and max count)

Write a TLibrary class with methods CheckOut and Return that raise these exceptions appropriately. Write a main program that demonstrates catching each exception type.


Part D: Design Challenges

D1. Transaction-Safe Operations Design a TTransactionLog class that records operations. If an exception occurs during a series of operations, the log should "roll back" by undoing completed operations. Implement BeginTransaction, CommitTransaction, and RollbackTransaction methods. Write a demonstration showing a transaction that succeeds and one that fails partway through.

D2. Robust CSV Parser Write a procedure ParseCSVFile(const FileName: String) that reads a CSV file where each row should have exactly 4 columns: Name (string), Age (integer), Grade (float), Passed (boolean: 'true'/'false'). Handle all possible errors: - File not found - Wrong number of columns in a row - Invalid integer/float/boolean values - Empty rows

For each row, either process it successfully or print a detailed error message and skip to the next row. At the end, print a summary: "Processed X of Y rows successfully."

D3. Retry Pattern Write a generic RetryOperation procedure that takes a procedure variable and retries it up to N times if it raises an exception, waiting progressively longer between retries. After all retries are exhausted, re-raise the last exception. Demonstrate with a simulated network operation that fails randomly.