FCF 2.0 development in progress...
> > > >
[News] [C++ Libraries API] [C++ Downloads] [Donate to the project] [Contacts]

Error Handling and Exception Management

The fcf framework features its own approach to handling errors that occur during function execution.

  • First, exceptions inherit from a single class, fcf::Exception, and carry information about the location where the exception occurred, while the error message text is generated based on a template with argument substitution (Example: "Invalid value: ${{1}}$")
  • Second, the framework allows writing functions that can operate in two modes simultaneously: throwing an exception upon error or recording error information without throwing an exception. This is implemented through the fcf::Error object. This approach enables rapid error handling without the overhead of expensive exception creation and propagation operations.
Extending the System: Custom Exceptions

The foundation of the error system lies in the fcf::Exception class. All exceptions must inherit from this class. To implement your own error types, you don't need to write complex classes from scratch; instead, you can use the FCF_EXCEPTION_DECLARE macro to create specialized exception types that inherit from FCF_EXCEPTION_DECLARE(MathException, "MATH_ERR", "Mathematical error: ${{1}}$", 1);

Each parameter in this declaration is crucial for creating a meaningful error report:

  • First parameter: The name of the created exception class.
  • Unique Identity: The second parameter (e.g., "MATH_ERR") defines the unique string identifier returned by the name method. This is vital for programmatic error categorization.
  • Dynamic Message Substitution: The third parameter is a message template. Using placeholders like ${{1}}$, ${{2}}$, etc., you can inject real-time data (like variable values or names) into the error message at the moment it is raised.
  • Argument Count: The final parameter specifies how many arguments the exception class will store to fulfill the placeholders in your template.

By leveraging this mechanism, your custom exceptions become more than just static strings—they become context-aware diagnostic tools that tell you not just what went wrong, but with what values it happened.

The fcf::Error Concept: Dual-Mode Reporting

While fcf::Exception provides the rich data, fcf::Error acts as the carrier. It is a lightweight container that can exist in two distinct modes, controlled by the loud property. This allows you to decide, on a per-call basis, whether an error should be a silent event or a full-blown exception.

  • Quiet Mode (loud = false): The error is recorded within the object (setting the error name and populating the exception buffer), but the program execution continues normally. This is ideal for high-performance, non-critical paths.
  • Loud Mode (loud = true): The error is recorded AND a C++ throw is performed immediately. This is used for critical failures that must interrupt the control flow.

Another significant feature of the fcf::Error class is the ability to avoid creating an exception object in quiet mode. If the fcf::Error::exception field is not set, no exception object will be instantiated; instead, only the error name will be recorded in the object. This allows for high-speed error handling without the time-consuming process of exception creation and invocation.

The error state in a fcf::Error object can be set by calling the fcf::Error::raise method. This method records the error data into the object and, if necessary, triggers an exception.

Dual-Mode Function Signatures

The most powerful way to utilize this capability is through function overloading. This allows you to design a single logical function that satisfies both high-performance and exception-safe requirements seamlessly.

Consider this implementation pattern:

// 1. Core logic: accepts an existing error object by reference (Quiet/Manual mode) int someFunction(int a_arg, fcf::Error& a_error); // 2. Public interface: provides a default 'loud' error object (Loud/Standard mode) inline int someFunction(int a_arg, fcf::Error&& a_error = fcf::Error{true}){ return someFunction(a_arg, a_error); }

When you call someFunction(5), the compiler selects the second overload, making the call "loud" by default. If you instead pass a pre-constructed fcf::Error object with loud = false, the compiler selects the first overload, allowing for "quiet" error management.

Comprehensive Example

The following example demonstrates how to implement this pattern and how to switch between modes in practice.

#define FCF_BASIS_IMPLEMENTATION #include <fcfBasis/basis.hpp> #include <iostream> // 1. Define a custom exception with a template message FCF_EXCEPTION_DECLARE(MathException, "MATH_ERR", "Mathematical error: ${{1}}$", 1); // 2. Implement the dual-mode pattern via overloading // Core implementation: handles error recording via reference int divide(int a_dividend, int a_divisor, fcf::Error& a_error) { if (a_divisor == 0) { // This populates a_error and optionally throws if a_error.loud is true a_error.raise<MathError>(__FILE__, __LINE__, "Division by zero"); return 0; } return a_dividend / a_divisor; } // Public interface: provides the default 'loud' behavior int divide(int a_dividend, int a_divisor, fcf::Error&& a_error = fcf::Error{true}) { return divide(a_dividend, a_divisor, a_error); } int main() { // --- SCENARIO 1: The Loud Mode (Standard Exception Handling) --- // Using the default signature: it will throw an exception. try { std::cout << "Attempting loud division..." << std::endl; divide(10, 0); } catch (const fcf::Exception& e) { std::cout << "Caught expected exception:\n" << e.what() << std::endl; } std::cout << std::endl; // --- SCENARIO 2: The Quiet Mode (Manual Error Management) --- // Passing an lvalue error object: it will only record the error. fcf::Error quietErr; // loud is false by default std::cout << "Attempting quiet division..." << std::endl; int result = divide(10, 0, quietErr); if (quietErr) { std::cout << "Error detected!" << std::endl; std::cout << "Error Name: " << quietErr.error << std::endl; } else { std::cout << "Operation successful." << std::endl; } return 0; }
Deep Diagnostics with fcf::Exception

Once an error is captured, the resulting fcf::Exception object provides a complete diagnostic report:

  • what(): Returns a fully formatted string including the resolved message and the source position.
  • message(): Provides the interpolated message after template substitution.
  • position(): Returns the exact file:line location.
  • full(): Returns a hierarchical report if the exception contains nested sub-exceptions.