CS253: Software Development with C++

Spring 2023

Assertions

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()

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";

Name a place that’s not in executable code.
  • between functions, where you could declare a global variable
  • in a class declaration, outside of methods

assert() example

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

assert() is a macro, not a function.

assert() must be a macro, created with #define trickery, in order to get the condition into the error message. Macros don’t play by namespace rules, so std::assert does not exist:

assert(3 > 2);
std::assert(4 < 5);  // 🦡
In file included from /usr/local/gcc/11.2.0/include/c++/11.2.0/cassert:44,
                 from /usr/local/gcc/11.2.0/include/c++/11.2.0/x86_64-pc-linux-gnu/bits/stdc++.h:33,
                 from /s/bach/a/class/cs000/public_html/pmwiki/cookbook/c++-includes.h:7,
                 from <command-line>:
c.cc:2: error: expected unqualified-id before ‘(’ token

In general, macros are evil, and should be replaced with constexpr for values, or simple functions. However, sometimes, you need a macro to do the dirty work.

When to use assert()

Disabling assertions

If you’re concerned about the run-time cost of assertions:

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()

static_assert() is like assert(), but:

static_assert() example

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() string literal

int value = 42;
static_assert(sizeof(int)==8, "must be exactly " + to_string(value));
c.cc:2: error: expected ‘)’ before ‘+’ token

Not even a C++ string literal:

static_assert(sizeof(int)==8, "must be exactly 8"s);
c.cc:1: internal compiler error: Segmentation fault
0xd23c5f crash_signal
	../../gcc-11.2.0/gcc/toplev.c:327
0x83eb18 finish_static_assert(tree_node*, tree_node*, unsigned int, bool, bool)
	../../gcc-11.2.0/gcc/cp/semantics.c:10134
0x7bc67a cp_parser_static_assert
	../../gcc-11.2.0/gcc/cp/parser.c:15461
0x7c7c0d cp_parser_declaration_statement
	../../gcc-11.2.0/gcc/cp/parser.c:13599
0x7c81bb cp_parser_statement
	../../gcc-11.2.0/gcc/cp/parser.c:11829
0x7c9aad cp_parser_statement_seq_opt
	../../gcc-11.2.0/gcc/cp/parser.c:12196
0x7c9b60 cp_parser_compound_statement
	../../gcc-11.2.0/gcc/cp/parser.c:12145
0x7e5b9a cp_parser_function_body
	../../gcc-11.2.0/gcc/cp/parser.c:24089
0x7e5b9a cp_parser_ctor_initializer_opt_and_function_body
	../../gcc-11.2.0/gcc/cp/parser.c:24140
0x7e6d4a cp_parser_function_definition_after_declarator
	../../gcc-11.2.0/gcc/cp/parser.c:30085
0x7e7eac cp_parser_function_definition_from_specifiers_and_declarator
	../../gcc-11.2.0/gcc/cp/parser.c:30001
0x7e7eac cp_parser_init_declarator
	../../gcc-11.2.0/gcc/cp/parser.c:21664
0x7c5da4 cp_parser_simple_declaration
	../../gcc-11.2.0/gcc/cp/parser.c:14464
0x7efef5 cp_parser_declaration
	../../gcc-11.2.0/gcc/cp/parser.c:14161
0x7f0bce cp_parser_toplevel_declaration
	../../gcc-11.2.0/gcc/cp/parser.c:14190
0x7f0bce cp_parser_translation_unit
	../../gcc-11.2.0/gcc/cp/parser.c:4942
0x7f0bce c_parse_file()
	../../gcc-11.2.0/gcc/cp/parser.c:45321
0x8c162d c_common_parse_file()
	../../gcc-11.2.0/gcc/c-family/c-opts.c:1218
Please submit a full bug report,
with preprocessed source if appropriate.
Please include the complete backtrace with any bug report.
See <https://gcc.gnu.org/bugs/> for instructions.

static_assert() optional message

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 have static_assert() show the file name and line number so the programmer can look it up themself. It’s a tradeoff between good error messages and development time.

static_assert(sizeof(bool)==7);
c.cc:1: error: static assertion failed