Skip to content

Latest commit

 

History

History
119 lines (86 loc) · 3.67 KB

ErrorHandling.md

File metadata and controls

119 lines (86 loc) · 3.67 KB

Error Handling

CCC uses a custom type template for error handling called Result which packages together a return value and a pointer to error information. This allows errors to be treated as values, and enables CCC to be compiled in environments where C++ exceptions are disabled.

It is defined like so:

class [[nodiscard]] CCC_WARN_UNUSED Result {
	...
protected:
	Value m_value;
	std::unique_ptr<Error> m_error;
	...
};

Note the nodiscard attribute. This makes it so if you ignore the return value of a function returning a Result object, the compiler will generate a warning.

Additionally, the CCC_WARN_UNUSED macro makes it so that if you assign a returned Result object to a local variable but do not use the local variable, supported compilers will generate a warning in that case too.

These two warnings in combination make these Result types much easier to use.

Basic Usage

As an example, lets say you wrote a "safe divide" function that returns an error if you try to divide by zero:

Result<f32> safe_divide(f32 x, f32 y)
{
	CCC_CHECK(y != 0, "Division by zero!");
	return x / y;
}

Here, the CCC_CHECK macro will return a Result object containing the error message Division by zero! if y is equal to zero.

Now lets say you wanted to call this function from within another:

Result<f32> force_due_to_gravity(f32 m1, f32 m2, f32 r)
{
	Result<f32> frac = safe_divide(m1 * m2, r * r);
	CCC_RETURN_IF_ERROR(frac);
	
	return BIG_G * *frac;
}

The CCC_RETURN_IF_ERROR macro lets you propagate errors up the call stack, even for Result types containing different types of values.

Note that to access the value stored in a Result object you can use the dereference operator (*), or the arrow operator (->). However, if you try to do this on a Result object that does not contain a value, this will trigger an assertion failure.

Macros

CCC provides various error handling macros. They are listed below:

User-Facing Fatal Errors

These should be used to indicate a fatal error that was caused by the user. For example, an incorrect command line argument being passed. These should not be used in the CCC library (the code in src/ccc/) itself.

CCC_EXIT

Print an error message and exit.

CCC_EXIT_IF_FALSE

Print an error message and exit, but only if the condition passed as the first argument to the macro evaluates to false.

CCC_EXIT_IF_ERROR

Print an error message and exit, but only if the provided Result object contains an error.

Internal Fatal Errors

These should be used for internal logic bugs only. These should not be used for problems that could be caused by corrupted input data (e.g. symbol tables).

CCC_ABORT_IF_FALSE

Print an error message and abort, but only if the condition passed as the first argument to the macro evaluates to false.

CCC_ASSERT

Print the condition passed as the first argument to the macro and abort, but only if said condition evaluates to false.

Recoverable Errors

These should be used for handling corrupted input data, and should be favoured for errors that occur in the CCC library (the code in src/ccc/) itself.

CCC_FAILURE

Generate a Result object containing the provided error message.

CCC_CHECK

Return a Result object containing the provided error message, but only if the condition passed as the first argument to the macro evaluates to false.

CCC_RETURN_IF_ERROR

Return from the current function if the provided Result object contains an error. This is used to propagate error information up the call stack.

Testing

CCC_GTEST_FAIL_IF_ERROR

Cause the current unit test to fail if the provided Result object contains an error.