Show Lecture.Assertions as a slide show.
CS253 Assertions
Assertion failure!
You’re out of here!
Overview
There are two sorts of assertions in C++:
They both come from <cassert>.
assert() is a preprocessor macro (!) that is, essentially:
void assert(bool condition) {
if (!condition) {
cerr << "assertion failed: ''name-of-condition''\n"
abort();
}
}
- It’s a runtime condition.
- It has to be in executable code.
- That is, anywhere you could say:
cout << "hi there\n";
- What would be a place that’s not in executable code?
void delete_file(const std::string &fname) {
assert(!fname.empty());
remove(fname.c_str());
}
int main() {
delete_file("tempfile");
delete_file("");
}
a.out: c.cc:2: void delete_file(const string&): Assertion `!fname.empty()' failed.
SIGABRT: Aborted
- Do not use assert() for dealing with user errors.
- Only use it for “impossible” circumstances:
- Make sure that a function argument, representing a size, is positive.
- Make sure that a pointer argument isn’t null.
- etc.
Disabling assertions
If you’re concerned about the run-time cost of assertions:
- You’re just fretting without data. Knock it off.
- The preprocessor symbol
NDEBUG
, which stands for
No DEBUGging, disables assertions, when defined.
- Use
#define NDEBUG
or g++ -DNDEBUG
to define NDEBUG
.
- Make sure that your assertions have no side effects.
Your code must produce the same results even if the assertions
are all disabled.
Avoid side effects
The assert() expression must not have side effects:
% cat ~cs253/Example/assert.cc
#include <iostream>
#include <cassert>
using namespace std;
int main() {
int n = 42;
assert(++n > 10); // 🦡
cout << n << '\n';
}
% g++ -Wall ~cs253/Example/assert.cc
% ./a.out
43
% g++ -DNDEBUG -Wall ~cs253/Example/assert.cc
% ./a.out
42
static_assert() is like assert(), but:
- It’s a compile-time test. It “executes” at compile time.
- It has zero run-time cost, because … compile-time.
- It fails like a syntax error fails.
- It doesn’t have to be in executable code.
- might be in a class definition
- might be outside of any function or class
- It takes two arguments:
- a condition
- an error message string (optional starting in C++17)
static_assert(-1 >> 1 == -1, "right shift must preserve sign");
int main() {
cout << "Hello, world!\n";
static_assert(sizeof(short)==2, "short must be 16 bits");
return 0;
}
static_assert(sizeof(int)==3, "int must be 24 bits");
c.cc:9: error: static assertion failed: int must be 24 bits
static_assert(sizeof(bool)==7, "I need 56-bit booleans!");
c.cc:1: error: static assertion failed: I need 56-bit booleans!
In C++ 2017, the message in static_assert() became optional.
Sometimes, it’s just not worth the trouble of writing the
verbose error message, and it’s good enough to give a file name
and line number so the programmer can look it up themself.
It’s a tradeoff between good error messages and code brevity.
static_assert(sizeof(bool)==7);
c.cc:1: error: static assertion failed