Library Configuration¶
The following C preprocessor defines can be used to configure various aspects of the library behaviour.
These must be set before #include
-ing any Flux headers.
Important
All translation units using Flux which are intended to be linked together must use the same configuration flags, otherwise ODR violations will occur.
It is strongly recommended to use your build system to pass the same flags to all TUs in your project, for example by using target_compile_definitions()
in CMake
Runtime Error Policy¶
When normal execution of a program cannot continue, Flux will raise a runtime error. Typically this happens because the library has detected a situation that would otherwise lead to undefined behaviour – for example, an out-of-bounds read of a sequence or a dereference of an empty flux::optional
. A runtime error will be handled according to the configured error policy: one of terminate, fail fast or unwind. The default error policy is terminate.
Terminate¶
-
FLUX_TERMINATE_ON_ERROR¶
-
FLUX_PRINT_ERROR_ON_TERMINATE¶
If FLUX_TERMINATE_ON_ERROR
is defined, a Flux runtime error will result in a call to std::terminate()
. This will in turn run the currently set terminate handler before halting the process.
By default, the library will attempt to print a short message to stdout
describing the error before terminating. This can be disabled by setting FLUX_PRINT_ERROR_ON_TERMINATE
to 0
.
Fail Fast¶
-
FLUX_FAIL_FAST_ON_ERROR¶
Alternatively FLUX_FAIL_FAST_ON_ERROR
is defined, the library will attempt to halt the running process in the fastest way possible, typically by executing an illegal CPU instruction. No cleanup will occur, no debug info will be printed to the console, and no exit handlers will be called.
Using the fail fast policy typically results in the smallest binary code size.
Unwind¶
-
FLUX_UNWIND_ON_ERROR¶
-
struct unrecoverable_error : std::logic_error¶
If FLUX_UNWIND_ON_ERROR
is defined, a runtime error will result in an exception being thrown, allowing “graceful shutdown” by unwinding the call stack and running destructors of automatic lifetime variables along the way. The exception has type flux::unrecoverable_error
, inheriting from std::logic_error
(and ultimately from std::exception
). Its what()
message contains a short description of what went wrong.
Attention
A runtime error means that a serious problem has occurred and the program cannot continue.
Stack unwinding is intended to be used to allow controlled shutdown as opposed to abrupt termination, much like a “panic” in languages such as Rust and Go. As the name suggests, an exception of type unrecoverable_error
should never just be “caught and ignored”.
Note
According to the C++ standard, it is unspecified whether stack unwinding occurs if an exception is not caught – an implementation may choose to immediately call std::terminate()
without performing unwinding if there is no matching catch clause anywhere in the call stack.
If using the “unwind” policy, you may also wish to wrap your main()
in an appropriate try-catch block to ensure unwinding occurs on all platforms.
Debug Assertions¶
-
FLUX_ENABLE_DEBUG_ASSERTS¶
As with many libraries, Flux has extra “sanity check” assertions which are not critical but may detect implementation bugs or unexpected behaviour. By default, these extra checks are enabled in debug builds and disabled in release builds – that is, they follow whether the NDEBUG
macro is set.
Setting FLUX_ENABLE_DEBUG_ASSERTS
to 1
will enable extra checks even in release builds, while setting it to 0
will disable them even in debug builds.
Static Bounds Checking¶
-
FLUX_DISABLE_STATIC_BOUNDS_CHECKING¶
On supported compilers, Flux can use compiler extensions to turn certain runtime bounds checks which would always fail into compile-time errors – see this blog post for more details.
Defining the macro FLUX_DISABLE_STATIC_BOUNDS_CHECKING
will disable this functionality, so that a runtime error will occur instead regardless of the compiler and optimisation settings.
Default Integer Type¶
-
FLUX_INT_TYPE¶
Flux uses a single signed integer type, aliased as distance_t
, for all sizes, distances, offsets etc in the library. By default, this is the same as std::ptrdiff_t
, but may be customised by defining FLUX_INT_TYPE
as the desired type. For example, you can use this macro to tell Flux to use 64-bit sizes even on a system with a 32-bit ptrdiff_t
:
#define FLUX_INT_TYPE std::int64_t
#include <flux.hpp>
static_assert(std::same_as<flux::distance_t, std::int64_t>);
A custom FLUX_INT_TYPE
must be a built-in signed integer type at least as large as std::ptrdiff_t
.
Numeric Error Policies¶
Flux provides a selection of checked integer functions, which are used internally by the library when performing operations on ints. The behaviour of these functions can be customised by setting the overflow, divide by zero and integer cast policies as desired.
Overflow policy¶
-
FLUX_ERROR_ON_OVERFLOW¶
-
FLUX_WRAP_ON_OVERFLOW¶
-
FLUX_IGNORE_OVERFLOW¶
If FLUX_ERROR_ON_OVERFLOW
is set, an integer operation which would overflow will instead raise a runtime error. This is the default in debug builds (i.e. NDEBUG
is not set).
Alternatively, if FLUX_WRAP_ON_OVERFLOW
is set, integer operations are performed as if by casting to the equivalent unsigned type, performing the operation, and then casting back to the original type. This avoids undefined behaviour (since overflow is well defined on unsigned ints) and avoids needing to generate error handing code, at the cost of giving numerically incorrect answers if overflow occurs. This is the default in release builds (i.e. NDEBUG
is set).
Finally, if FLUX_IGNORE_OVERFLOW
is set, the standard built-in integer operations will be used. This means that an operation which overflows will result in undefined behaviour. Use this setting if you are already handling signed integer UB by some other means (for example compiling with -ftrapv
or using UB Sanitizer) and wish to avoid “double checking”.
Divide by zero policy¶
-
FLUX_ERROR_ON_DIVIDE_BY_ZERO¶
-
FLUX_IGNORE_DIVIDE_BY_ZERO¶
If FLUX_ERROR_ON_DIVIDE_BY_ZERO
is set then a runtime error will be raised if zero is passed as the second argument to flux::num::div()
or flux::num::mod()
. This is the default in debug builds.
Alternatively, if FLUX_IGNORE_DIVIDE_BY_ZERO
is set then no extra zero check will be used in flux::num::div()
or flux::num::mod()
. This is the default for release builds.
Integer cast policy¶
-
FLUX_INTEGER_CAST_POLICY_CHECKED¶
-
FLUX_INTEGER_CAST_POLICY_UNCHECKED¶
If FLUX_INTEGER_CAST_POLICY_CHECKED
is defined, then flux::num::cast<To>(from) will (if necessary) perform a runtime check to ensure that the source value is within the bounds of the destination type – that is, that the cast is not lossy. This is the default for debug builds.
Alternatively FLUX_INTEGER_CAST_POLICY_UNCHECKED
is defined then no runtime check will occur, and flux::num::cast<To>(from) is equivalent to a plain static_cast<To>(from). This is the default in release builds.