Some tools and techniques for detecting, reporting and investigating errors and unexpected behavior in C++ code.
Pre-Processor Macros
An old method of getting information about the file and line where a thing occurred applied pre-processor macros:
__FILE__: A macro that expands to the name of the current input file.__LINE__: A macro that expands to the current input line number.
|
|
Output:
/xyz/example.cpp (15): Hello world!
/xyz/example.cpp (10): Message from a different function
std::source_location
Since ISO C++ 2020, there is a new and better way of getting this information: std::source_location
|
|
Output:
/xyz/example.cpp (19:5): int main(int, char **): Hello world!
/xyz/example.cpp (14:5): void func(int): Message from a different function
std::stacktrace
Another tool that may be handy sometimes: std::stacktrace.
That requires ISO C++ 2023 support (and at the time of writing, also linking with sometimes different libraries).
|
|
Output:
=========================================
Stacktrace (complete):
0# main at /app/example.cpp:30
1# at :0
2# __libc_start_main at :0
3# _start at :0
4#
Message: Hello, world!
-----------------------------------------
Stacktrace (individual elements of each entry):
main - File: /app/example.cpp (Line: 30)
- File: (Line: 0)
__libc_start_main - File: (Line: 0)
_start - File: (Line: 0)
- File: (Line: 0)
=========================================
Stacktrace (complete):
0# func(int) at /app/example.cpp:25
1# main at /app/example.cpp:31
2# at :0
3# __libc_start_main at :0
4# _start at :0
5#
Message: Message from a different function
-----------------------------------------
Stacktrace (individual elements of each entry):
func(int) - File: /app/example.cpp (Line: 25)
main - File: /app/example.cpp (Line: 31)
- File: (Line: 0)
__libc_start_main - File: (Line: 0)
_start - File: (Line: 0)
- File: (Line: 0)
static_assert() and assert()
Assertions are helpful features for testing/debugging and for noticing errors/bugs that should never happen.
- If the expression evaluates to true, the assertion statement does nothing.
- If the conditional expression evaluates to false, an error message is displayed and the program is terminated (→ using
assert) or the compilation fails (→ usingstatic_assert).
An assertion doesn’t allow to recover from an error (which, after all, should never had happenend in the first place).
static_assert()
A keyword/declaration that checks a software assertion at compile-time.
If the specified constant expression is false, the compilation fails with an error and displays the specified message (if one is provided).
static_assert(bce, msg); // Available since C++11.
static_assert(bce); // Since C++17, the message parameter is optional.
- The parameter bce must be an integral constant expression that can be converted to a boolean type (i.e.
trueorfalse) and must be a constant known at compile time. - The parameter msg is the error message that is displayed when the expression parameter is
false.
(Since it has to be a string literal, it cannot contain dynamic information or even a constant expression that is not a string literal itself. In particular, it cannot contain the name of the template type argument.) - Since
static_assert()is a built-in compile-time test, it works regardless whether you build in Release or Debug mode. - One can use the
static_assertkeyword at namespace, class, or block scope.
(Thestatic_assertkeyword is technically a declaration, even though it does not introduce new name into the program, because it can be used at namespace scope.)
assert()
A function-like pre-processor macro that checks a software assertion at run-time.
Evaluates an expression and if the result is false, aborts/terminates the program (and usually prints a diagnostic message).
// #define NDEBUG // uncomment to disable assert()
#include <cassert>
assert(condition);
- The parameter condition must be an expression of scalar type that is interpreted to being either
trueorfalse. - The definition of the macro depends on another macro (
NDEBUG), which is not defined by the standard library:
IfNDEBUGis defined as a macro name at the point in the source code where<cassert>(or<assert.h>) is included, thenassert()is deactivated and does nothing (that is usually the case in Release mode). Most build environments take care of this automatically when you switch between Debug and Release builds.
Important: Because of this volatility, make sure that you do not base the control flow of your code on whetherassert()is enabled or disabled!
There is no standardized interface to add an additional message to assert errors, but there are workarounds:
assert((2 + 2 == 4, "Message")); // -> 1.
assert(condition && "Message"); // -> 2.
- A portable way is to use a comma operator (provided it has not been overloaded).
- Combining a string literal (which always evaluates to true) by logical AND with the condition doesn’t impact the evaluation of the assert.
Film & Television (57)
How To (70)
Journal (18)
Miscellaneous (4)
News & Announcements (21)
On Software (12)
Projects (26)