In a function that takes parameters, the caller may be able pass in arguments that are syntactically valid but semantically meaningless. For example, in the previous lesson (7.15 -- Detecting and handling errors, we showed the following sample function:
1 2 3 4 5 6 7 |
void printDivision(int x, int y) { if (y != 0) std::cout << static_cast<double>(x) / y; else std::cerr << "Error: Could not divide by zero\n"; } |
This function does an explicit check to see if y
is 0
, since dividing by zero is a semantic error and will cause the program to crash if executed.
In the prior lesson, we discussed a couple of ways to deal with such problems, including halting the program, or skipping the offending statements.
Both of those options are problematic though. If a program skips statements due to an error, then it is essentially failing silently. Especially while we are writing and debugging programs, silent failures are bad, because they obscure real problems. Even if we print an error message, that error message may be lost among the other program output, and it may be non-obvious where the error message is being generated or how the conditions that triggered the error message occurred. Some functions may be called tens or hundreds of times, and if only one of those cases is generating a problem, it can be hard to know which one.
If the program terminates (via std::exit
) then we will have lost our call stack and any debugging information that might help us isolate the problem. std::abort
is a better option for such cases, as typically the developer will be given the option to start debugging at the point where the program aborted.
Preconditions, invariants, and postconditions
In programming, a precondition is any condition that must always be true prior to the execution of component of code. Our check of y
is a precondition that ensures y
has a valid value before the function continues.
It’s more common for functions with preconditions to be written like this:
1 2 3 4 5 6 7 8 9 10 |
void printDivision(int x, int y) { if (y == 0) { std::cerr << "Error: Could not divide by zero\n"; return; } std::cout << static_cast<double>(x) / y; } |
An invariant is a condition that must be true while some component is executing.
Similarly, a postcondition is something that must be true after the execution of some component of code. Our function doesn’t have any postconditions.
Assertions
Using a conditional statement to detect an invalid parameter (or to validate some other kind of assumption), along with printing an error message and terminating the program, is such a common response to problems that C++ provides a shortcut method for doing this.
An assertion is an expression that will be true unless there is a bug in the program. 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 (via std::abort
). This error message typically contains the expression that failed as text, along with the name of the code file and the line number of the assertion. This makes it very easy to tell not only what the problem was, but where in the code the problem occurred. This can help with debugging efforts immensely.
In C++, runtime assertions are implemented via the assert preprocessor macro, which lives in the <cassert> header.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <cassert> // for assert() #include <cmath> // for std::sqrt #include <iostream> double calculateTimeUntilObjectHitsGround(double initialHeight, double gravity) { assert(gravity > 0.0); // The object won't reach the ground unless there is positive gravity. if (initialHeight <= 0.0) { // The object is already on the ground. Or buried. return 0.0; } return std::sqrt((2.0 * initialHeight) / gravity); } int main() { std::cout << "Took " << calculateTimeUntilObjectHitsGround(100.0, -9.8) << " second(s)\n"; return 0; } |
When the program calls calculateTimeUntilObjectHitsGround(100.0, -9.8)
, assert(gravity > 0.0)
will evaluate to false
, which will trigger the assert. That will print a message similar to this:
dropsimulator: src/main.cpp:6: double calculateTimeUntilObjectHitsGround(double, double): Assertion `gravity > 0.0' failed.
The actual message varies depending on which compiler you use.
Although asserts are most often used to validate function parameters, they can be used anywhere you would like to validate that something is true.
Although we told you previously to avoid preprocessor macros, asserts are one of the few preprocessor macros that are considered acceptable to use. We encourage you to use assert statements liberally throughout your code.
Making your assert statements more descriptive
Sometimes assert expressions aren’t very descriptive. Consider the following statement:
1 |
assert(found); |
If this assert is triggered, the assert will say:
Assertion failed: found, file C:\\VCProjects\\Test.cpp, line 34
What does this even mean? Clearly something wasn’t found, but what? You’d have to go look at the code to determine that.
Fortunately, there’s a little trick you can use to make your assert statements more descriptive. Simply add a string literal joined by a logical AND:
1 |
assert(found && "Car could not be found in database"); |
Here’s why this works: A string literal always evaluates to Boolean true
. So if found
is false
, false && true
is false
. If found
is true
, true && true
is true
. Thus, logical AND-ing a string literal doesn’t impact the evaluation of the assert.
However, when the assert triggers, the string literal will be included in the assert message:
Assertion failed: found && "Car could not be found in database", file C:\\VCProjects\\Test.cpp, line 34
That gives you some additional context as to what went wrong.
NDEBUG
The assert
macro comes with a small performance cost that is incurred each time the assert condition is checked. Furthermore, asserts should (ideally) never be encountered in production code (because your code should already be thoroughly tested). Consequently, many developers prefer that asserts are only active in debug builds. C++ comes with a way to turn off asserts in production code. If the macro NDEBUG
is defined, the assert macro gets disabled.
Some IDEs set NDEBUG
by default as part of the project settings for release configurations. For example, in Visual Studio, the following preprocessor definitions are set at the project level: WIN32;NDEBUG;_CONSOLE
. If you’re using Visual Studio and want your asserts to trigger in release builds, you’ll need to remove NDEBUG
from this setting.
If you’re using an IDE or build system that doesn’t automatically define NDEBUG
in release configuration, add it in the project or compilation settings manually.
Some assert limitations and warnings
There are a few pitfalls and limitations to asserts. First, the assert itself can have a bug. If this happens, the assert will either report an error where none exists, or fail to report a bug where one does exist.
Second, your asserts should have no side effects -- that is, the program should run the same with and without the assert. Otherwise, what you are testing in a debug configuration will not be the same as in a release configuration (assuming you ship with NDEBUG).
Also note that the abort()
function terminates the program immediately, without a chance to do any further cleanup (e.g. close a file or database). Because of this, asserts should be used only in cases where corruption isn’t likely to occur if the program terminates unexpectedly.
Asserts vs error handling
Assertions and error handling are similar enough that their purposes can be confused, so let’s clarify:
The goal of an assertion is to catch programming errors by documenting something that should never happen. If that thing does happen, then the programmer made an error somewhere, and that error can be identified and fixed. Assertions do not allow recovery from errors (after all, if something should never happen, there’s no need to recover from it), and the program will not produce a friendly error message.
On the other hand, error handling is designed to gracefully handle cases that could happen (however rarely) in release configurations. These may or may not be recoverable, but one should always assume a user of the program may encounter them.
Best practice
Use assertions to document cases that should be logically impossible.
Assertions are also sometimes used to document cases that were not implemented because they were not needed at the time the programmer wrote the code:
1 |
assert(moved && "Need to handle case where student was just moved to another classroom"); |
That way, if a future user of the code does encounter a situation where this case is needed, the code will fail with a useful error message, and the programmer can then determine how to implement that case.
static_assert
C++11 added another type of assert called static_assert
. A static_assert is an assertion that is checked at compile-time rather than at runtime, with a failing static_assert
causing a compile error.
A static_assert
takes the following form:
static_assert(condition, diagnostic_message)
If the condition is not true, the diagnostic message is printed. Here’s an example of using static_assert to ensure types have a certain size:
1 2 3 4 5 6 7 |
static_assert(sizeof(long) == 8, "long must be 8 bytes"); static_assert(sizeof(int) == 4, "int must be 4 bytes"); int main() { return 0; } |
On the author’s machine, when compiled, the compiler errors:
1>c:\consoleapplication1\main.cpp(19): error C2338: long must be 8 bytes
Because static_assert
is evaluated by the compiler, the condition must be able to be evaluated at compile time. Also, unlike normal assert
(which is evaluated at runtime), static_assert
can be placed anywhere in the code file (even in global space).
In C++11 and C++14, the diagnostic message must be supplied as the second parameter. Since C++17, providing a diagnostic message is optional.
![]() |
![]() |
![]() |
just a minor observation. my English is bad so im not sure but this doesn't sound right
"then we have will have lost our call stack"
After header Assertions there is code
And there is no <iostream> which lead to compile error.
Fixed. Thanks!
Don't talk back, Sexy! This education is worth over $100k! It's an opensource textbook and it's fantastic! Minor, insignificant mistakes are bound to happen... Better than any commercial book you'd find at your local bookstore!
It's still good to report actual mistakes
you said assert is run time
you also said it is preprocossor.
does it conflict?
No. Assert is a preprocessor macro that the preprocessor converts into normal code (which is then compiled). That compiled code then executes at runtime.
For the last part on exception, "The error progressively moves up the stack until it is either caught and handled"
I think it should be move down the stack instead. If i'm wrong, may i know why thanks!
Will learncpp ever add unit testing to it's course?
They have! https://www.learncpp.com/cpp-tutorial/introduction-to-testing-your-code/
I was wondering why
did not work for me and see in the comments that several others had the same problem. Please add to the lesson the info that the NDEBUG must be defined before <cassert> is included.
You're not supposed to define NDEBUG in code, the lesson wasn't clear about this. I updated it to say that NDEBUG is to be defined at project level in the compiler options.
I see, thanks for the clarification!
I have a question:
>>Because static_assert is not evaluated at runtime, static_assert statements can also be placed anywhere in the code file (even in global space).
Why can't we have assert in global space? how does this has anything to do with running in run-time or compile-time node?
Thanks
I believe there's an error in your first example here:
As you can see, the example uses the std::array found in the <array> header; however, the header is not included.
Thanks! That example didn't make much sense, and would've needed too many casts to keep. I replaced it entirely.
Hi ,
Is #define NDEBUG works only on assert and not on static_assert.And if it is so, how can we turn of static_assert?
static_assert has no performance impact, because it is executed during compile time. Therefore you don't have to turn it off, if you're worried about performance.
However, if for some reason, you still want to turn it off when NDEBUG is defined, you can always do something like this:
You just have to enclose every static_assert which you want to conditionally deactivate in this kind of preprocessor condition. This is called "conditional compilation" and a very useful tool that you'll probably use a ton later anyway, so don't worry about how it looks. The preprocessor has its uses ;)
Thankyou for the explanation !
Something to consider: static_assert and templates have an interesting relationship, especially for class templates. Only those member functions are compiled that are instantiated, so that only static_asserts in instantiated functions have any effect. It makes perfect sense, but it still tripped me up (when writing a CRTP hierarchy).
Debug Error!
abort() has been called
When using assert I am getting this error ! what does that mean ?
That's what `assert` is supposed to do. It terminates your process if the condition evaluates to false.
1. For some reason the assert still works. I'm using the newest Visual Studio.
2. Just remembered a problem that happened a while back when I was writing the "blackjack game". Something was under my function and I got an error and then realized that it should be over it. But the error message was not really understandable. So I just wanna ask what is the best order to write all the things that we covered outside the main function?
For example:
#include <EXAMPLE>
Global variables (1)
Enum (2)
Struct (3)
Function (4)
int main()
{
}
1. to disable assertions.
`NDEBUG` needs to be defined before you include
2.
There's no general rule. What I do, and what works well
I'm having a bit of trouble with assert.
When I use assert , the program crashes(exe has stopped working) and the command prompt window shows the following line :
'This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information. '
The process then returns 3.
That's bizarre. What OS and compiler are you using?
OS - Windows 7 Ultimate 32-bit
Compiler - GNU GCC
IDE - Code:: Blocks 17.12
Can you share the code that is crashing for you?
I'm guessing that's because of the ..erm emulation (if that's what it is) that GCC (or Cygwin) on windows uses. Native builds work fine (MSVC and Clang).
Also this previous comment here (similar issue)
https://www.learncpp.com/cpp-tutorial/7-12a-assert-and-static_assert/comment-page-1/#comment-294033
Hi,
One thing about this line:
"An assert statement is a preprocessor macro that evaluates a conditional expression at runtime."
Isn't preprocessor macro supposed to guide the compiler to do something at compile-time? Does the preprocessor replace the assert statement with something that tells the compiler that the following piece of code should be evaluated at run-time and that the compiler should ignore it?
The preprocessor replaces ever use of @assert with C++ code, that is then compiled by the compiler along all the other code.
Wait, I thought preprocessor directives are directly evaluated by the preprocessor itself (ex: ifdef, ifndef etc...) and since the preprocessor can check the condition during this stage, I'm led to think a failed static_assert causes the 'compiler' to not even go past the preprocessor stage (consequently no machine code is generated implying no 'compilation' occurs).
`static_assert` is no macro, the preprocessor doesn't do anything with it.
`assert` has to be a macro to access the __LINE__ etc. macros in the right place. The preprocessor expands it, but it doesn't do any checks.
Whoops my bad!
Okay the second one makes sense!
Thank you!
On static assert I see some possibilities, since I prefer to work open source (almost all the code I wrote the past few years can be found on github.com), I deem it possible that (although I do use either make files or compilation shell-scripts to 'force' people to do it right), some issues with config can come up, and not everybody downloading and building from source is always well versed in programming in general, and some errors can be easier to help with if you know what went wrong.
For example can static_assert() be used to determine if you are compiling with C++11 and thus throw a compiler error when older (or newer) versions of C++ are used?
Something like this ;)
I am also thinking that if a .cpp file contains for Windows only and should not be included in Mac or Linux builds such an assertion can also be useful.
I mean, that will make sense to everybody, even those who know nothing at all about coding. An error saying certain windows only dependencies have not been found will only make sense to those who know their stuff. XD
(Of course, I am quite often surprised how many obvious things don't make sense to some people, but I guess 100% fool-proof doesn't exist as fools are so ingenious, but to most people, it should make sense).
And of course, I could also get on with all the flags you can give to the compiler...
You should CMake to do this. You can define which version of the standard you need and change compilation settings per OS (or deny compilation at all).
Most compilers predefine macros which could be used to detect the OS, but that's not standard.
Hi Alex!
Just a heads up that contracts will be added in C++20. This lesson and at least the lessons using the Fraction class should be updated.
Yup. Quite a few lessons will be impacted by C++20. If the modules functionality makes it in (and I hope it does), then pretty much every lesson will be impacted!
Hey Alex,
I've heard a lot about modules being added to C++20, and I've used modules in Java and Python before, but I'm not clear on what the benefits of modules are vs the #include preprocessor directive. Do you have any thoughts regarding the matter or can you link to any resources regarding this?
Hi Soham!
Headers are compiled in every file they're included in. Modules are compiled only once. That's faster and you no longer need to worry about include guards. Modules can't break each other by changing the import order. Headers were able to break each other by using #defines, since headers are entirely handled by the preprocessor.
Headers might expose entities that you intended only for internal use (In the situation of sharing libraries). Modules have full control over what is visible to the importer.
Including a header gives you everything from that header. Modules can be imported partially, so you only get what you need. This prevents cluttered autocomplete and potential name collisions.
This is what I remember, I haven't worked with modules yet. What I said might be wrong or incomplete.
Thanks a lot!
When will you be teaching Modules?
Hi Alex,
I was writing a piece of code which states
While I was testing this piece of code by writing
the console prints 'Assertion failed: invalidColor && ("Unrecognized color: " + color).c_str()'.
What I want to ask is that, is there a way I can make the assert message alter based on different parameter contents in the function call?
In this example, the ideal output on the console I would like to see is 'Assertion failed: invalidColor && "Unrecognized color: pink"'.
@assert is a compile-time macro, it's text content cannot be dynamically created.
You can throw an exception (Chapter 14) instead
Thanks @nascardriver, that saves my day. I really appreciate it!
I updated my previous post to use @std::invalid_argument instead of @std::runtime_error
Hey Alex,
Could you explain why #define NDEBUG has to be written before #include <cassert> ?
#define NDEBUG didn't work for my complier when I wrote it after #include <cassert>
Hi Davis!
When you include a file it's content is copied into the including file
turns into
Now, when you define NDEBUG after the include to <cassert>, the preprocessor has already checked whether or not it should enable asserts, that's why you need to define it before including <cassert>.
Oh. Makes perfect sense now. Thanks!
HI ALEX!
AS STATED IN 7.12a UNDER STATIC-ASSERT:
Because static_assert is not evaluated at runtime, static_assert statements can also be placed anywhere in the code file (even in global space).
WHY IS IT SO? AND IF THEY WERE EVALUATED AT RUNTIME WHAT PROBLEM MAY OCCUR PUTTING THEM IN GLOBAL SPACE..AND WHAT IS NOT BEEN FACED IN GLOBAL SPACE AS THEY ARE EVALAUTED AT COMPILE TIME?
It is so because that's how it was designed: The goal is to give programmers a way to flag things that you expect to be true at compile time rather than at runtime.
For example, if your code has an assumption that integers are at least 4 bytes, previously you could assert this, but it wouldn't fail until you ran the program. With static_assert, you can now fail your program at compile time, saving you from compiling something that won't run anyway.
Code execution always starts at the top of main() and then continues from there into whatever subfunctions are called. At no point is code in the global space ever executed.
I don't understand your last question.
you said,"static_assert is evaluated at compile time so we can put it in global space comfortably". I want to ask what problem maybe caused if we put it in global space if it would evaluate at runtime.
Code outside of functions won't be executed at runtime. All code needs to be in a function of some sort. static_assert can be outside of functions and is executed at compile time.
(There's also constexpr but I don't want to overcomplicate things)
THANKS ! NASCARDRIVER
yes plz explain 'constexp' i saw it in 8.11 i don't know what does this keyword do?
constexpr values and functions are evaluated at compile-time.
constexpr value are covered in lesson 2.9, there's no tutorial for constexpr functions yet.
You don't need to worry about constexpr functions just yet, learn the basics first.
Typo: "If the program calls GetArrayValue(-3),"
The function requires two arguments, but only one is provided. Also, the G is capitalized.
Thanks, fixed!
Hello Alex,
the first time I tried "#define NDEBUG" I used it after "#include <cassert>" and wondered why it didn't switch off all assert() afterwards:
I guess that's a really stupid mistake to make, but to avoid confusion, it might be a good idea to change the snippet in "NDEBUG and other considerations" into the following lines:
Anyway, thanks for the "to-the point" introduction of assert() and to use it effectively!
Hey there CrazyL!
This answer was answered already before. But you might not have seen it, so here it comes!
As said by nascardriver, when you include a file, the file is copied to the uncompiled code. The contents of cassert are something like this:
So, before you define NDEBUG, the if statement is false, and assert, is defined.
Don't credit me. Credit nascardriver.
"Exception handling is an advanced C++ topic, and we cover it in much detail in chapter 15 of this tutorial."
It's in chapter 14.
Could you explain me the terms of "production code" and "debug builds" ?
This is covered in lesson 0.6a. Have a read there.
What Do You Mean By this? :-
Because static_assert is not evaluated at run-time, static_assert statements can also be placed anywhere in the code file (even in global space).
Plz Explain !
And YES, I found that non-const vars don't work in static assertions.
Oh Okay ! I saw that static_assert works in global space while assert doesn't.
But Now, My question is that why run-time variables etc. don't work in global space?
Global and static variables get initialized when the program starts up. Then execution begins at the top of main and continued to the bottom. Code in global space would never get executed.
When code is executed at runtime, the execution path starts at the top of main() and continued to the bottom of main() (branching out for called functions). So for normal asserts, those need to be put inside a function that gets called in order to be executed at runtime.
However, static_assert is evaluated by the compiler when it's compiling the program. This means static_assert can be placed anywhere in the program, even outside of functions, since it doesn't have to be within the path of execution.
So It means that because compilation starts from the top of program(not from main()) that's why static assert is seen by it but because asserts are evaluated at run-time they are not seen as execution starts from the top of main()(not from the top of program)
Tell Me If I'm Right !
Yup.
can you explain what do you mean by this sentence? :)
In C++11, a diagnostic message must be supplied as the second parameter(it is written just above "Exceptions")
The diagnostic message is the second parameter provided to the static_assert function that gets printed if the condition isn't true. For example, "long must be 8 bytes" is a diagnostic message.
Hi,
I'm running the code below, but I don't get the message "Assertion failed: index >= 0 && index <=9, file...", rather, the program just crashes with the usual message box showing up. Is something wrong in the code?
Nothing is wrong in the code. My best guess is that you're running your code with a release configuration, and that's setting NDEBUG, which turns assertions off. Try recompiling and running your code using a debug configuration instead.
Ok, I found the problem, it’s compiler specific. MinGW is not directing the the diagnostic message to std::cout, but to a pop-up window. To see this pop up window the code has to be compiled with the mwindow switch, i.e. g++ -mwindow main.cpp, then it works as shown in the example.