Fundamentals 48 min read

A Comprehensive Overview of Exception and Error Handling Across Languages and Platforms

This article surveys various error and exception handling mechanisms in C, C++, Java, Go, Rust and other languages, comparing global error codes, return values, error stacks, and OS-level support, and evaluates their advantages and drawbacks for modern software development.

FunTester
FunTester
FunTester
A Comprehensive Overview of Exception and Error Handling Across Languages and Platforms

The article begins by distinguishing between errors (unrecoverable, low‑level failures) and exceptions (recoverable, program‑level issues) and explains why systematic error handling is essential for reliable software.

Global Error Codes

Traditional C‑style error handling relies on a global errno variable. An example shows how fopen sets errno to ENOENT when a file cannot be opened:

#include <stdio.h>
#include <errno.h>
int main() {
  FILE *file = fopen("nonexistent_file.txt", "r");
  if (file == NULL) {
    perror("Error opening file");
    printf("Error code: %d
", errno);
  } else {
    fclose(file);
  }
  return 0;
}

In multithreaded environments, errno is thread‑local, avoiding conflicts.

C++ Error Handling

C++11 introduced std::error_code and std::error_category to represent platform‑specific error conditions without global state. A custom error category for a fictional WxPay library is illustrated:

enum class WxPayErrorCode { kInvalidOpenTestFile = 0x1375212, kInvalidEmptyFile = 0x1375213 };
class WxPayErrorCategory : public std::error_category {
public:
  static const WxPayErrorCategory& Instance() {
    static thread_local WxPayErrorCategory instance;
    return instance;
  }
  const char* name() const noexcept override { return "WxPayErrorCategory"; }
  std::string message(int ev) const override {
    switch (static_cast<WxPayErrorCode>(ev)) {
      case WxPayErrorCode::kInvalidOpenTestFile: return "Cannot open test file!";
      case WxPayErrorCode::kInvalidEmptyFile: return "Empty file!";
      default: return "Unknown";
    }
  }
};
std::error_code make_error_code(WxPayErrorCode ec) {
  return {static_cast<int>(ec), WxPayErrorCategory::Instance()};
}

Using std::error_code allows functions to return rich error information without exceptions.

OpenSSL Error Stack

OpenSSL maintains an error stack that can be queried with ERR_get_error, ERR_peek_error, and ERR_error_string_n. A short example demonstrates pushing and printing errors:

#include <openssl/err.h>
void inner_foo() { ERR_put_error(ERR_LIB_EVP, 0, ERR_R_PEM_LIB, __FILE__, __LINE__); }
void foo() { inner_foo(); if (ERR_peek_error()) { ERR_put_error(ERR_LIB_EVP, 0, ERR_R_SYS_LIB, __FILE__, __LINE__); } }
int main() { BIO *bio = BIO_new_fp(stdout, BIO_NOCLOSE); foo(); unsigned long err = ERR_peek_error(); if (err) ERR_print_errors(bio); }

Visual Basic Error Handling

VB uses the global Err object and On Error statements (e.g., On Error GoTo ErrorHandler) to capture runtime errors, optionally displaying a dialog.

Return‑Value‑Based Error Handling

Many libraries (e.g., libcurl) return an error enum such as CURLcode. The article shows a C example that checks errno after fopen and prints the error message.

Modern Language Approaches

Go prefers returning (value, error) pairs and uses defer for cleanup:

f, err := os.Open("filename.txt")
if err != nil { log.Fatal(err) }
defer func() { if err := f.Close(); err != nil { log.Fatal(err) } }()

Rust uses Result<T, E> with the ? operator for concise propagation:

fn read_file(path: &str) -> Result<String, std::io::Error> {
  let mut file = File::open(path)?;
  let mut contents = String::new();
  file.read_to_string(&mut contents)?;
  Ok(contents)
}

C++23 introduces std::expected<T, E>, offering a value‑or‑error return without exceptions.

Boost.Outcome

Boost.Outcome provides a result<T> type with macro BOOST_OUTCOME_TRY to chain operations while handling errors.

Operating‑System Support

Windows implements Structured Exception Handling (SEH) and Vectored Exception Handling (VEH). SEH uses unwind tables ( UNWIND_INFO) for efficient stack unwinding, while VEH allows global interception of all exceptions via AddVectoredExceptionHandler and AddVectoredContinueHandler.

Conclusion

The article concludes by summarizing the trade‑offs of each approach and suggests that the choice of error‑handling strategy should align with project requirements, performance constraints, and the need for rich diagnostic information.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

RustException HandlingCError HandlingSEH
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.