Using Assert in Release Builds
The lesson mentions that assert()
calls are sometimes stripped out in release builds for performance. What if I want to keep some critical assertions enabled in release builds?
There are a few ways to keep certain assert()
calls active in release builds:
Option 1: Rather than defining the global NDEBUG
macro disable assert()
calls, we can implement our own conventions. For example, we could use #ifdef
directives with macros that have names like ENABLE_ASSERTIONS
and ENABLE_RELEASE_ASSERTIONS
:
#include <cassert>
int Divide(int x, int y) {
#ifdef ENABLE_RELEASE_ASSERTIONS
assert(y != 0 && "Cannot divide by zero");
#endif
return x / y;
}
We then have more granular control over which assertions are enabled, based on which definitions we provide in our project settings
Option 2: Create a custom assert-like macro that isn't disabled in release builds. In the following example, our RELEASE_ASSERT()
macro will be used whether or not NDEBUG
is defined:
#include <iostream>
#define RELEASE_ASSERT(expr) \
if (!(expr)) { \
std::cerr << "Assertion failed: " #expr; \
abort(); \
}
int Divide(int x, int y) {
RELEASE_ASSERT(y != 0);
return x / y;
}
int main() {
Divide(2, 0);
}
Assertion failed: y != 0
Option 3: Use logging libraries like spdlog or Boost.Log that offer assertion-like checks which remain enabled in release builds by default.
In general, the decision to keep assertions enabled in release builds is a tradeoff between performance and catching/logging critical errors. Enable them selectively for the most crucial checks.
Errors and Assertions
Learn how we can ensure that our application is in a valid state using compile-time and run-time assertions.