What Does Static_Assert Do, and What Would You Use It For

What does static_assert do, and what would you use it for?

Static assert is used to make assertions at compile time. When the static assertion fails, the program simply doesn't compile. This is useful in different situations, like, for example, if you implement some functionality by code that critically depends on unsigned int object having exactly 32 bits. You can put a static assert like this

static_assert(sizeof(unsigned int) * CHAR_BIT == 32);

in your code. On another platform, with differently sized unsigned int type the compilation will fail, thus drawing attention of the developer to the problematic portion of the code and advising them to re-implement or re-inspect it.

For another example, you might want to pass some integral value as a void * pointer to a function (a hack, but useful at times) and you want to make sure that the integral value will fit into the pointer

int i;

static_assert(sizeof(void *) >= sizeof i);
foo((void *) i);

You might want to asset that char type is signed

static_assert(CHAR_MIN < 0);

or that integral division with negative values rounds towards zero

static_assert(-5 / 2 == -2);

And so on.

Run-time assertions in many cases can be used instead of static assertions, but run-time assertions only work at run-time and only when control passes over the assertion. For this reason a failing run-time assertion may lay dormant, undetected for extended periods of time.

Of course, the expression in static assertion has to be a compile-time constant. It can't be a run-time value. For run-time values you have no other choice but use the ordinary assert.

What is the difference between assert and static_assert?

You ask three questions, so I will try to answer each of them.

  • Can someone give me an example of where only static_assert will work, or only assert?

static_assert is good for testing logic in your code at compilation time. assert is good for checking a case during run-time that you expect should always have one result, but perhaps could somehow produce an unexpected result under unanticipated circumstances. For example, you should only use assert for determining if a pointer passed into a method is null when it seems like that should never occur. static_assert would not catch that.

  • Do they do anything a simple if statement can't do?

assert can be used to break program execution, so you could use an if, an appropriate error message, and then halt program execution to get a similar effect, but assert is a bit simpler for that case. static_assert is of course only valid for compilation problem detection while an if must be programmatically valid and can't evaluate the same expectations at compile-time. (An if can be used to spit out an error message at run-time, however.)

  • Is it bad practice to use them?

Not at all!

static_assert usage in C++ vs C

  1. Something in one of your headers appears to be doing #define static_assert _Static_assert, which fails in C++ because _Static_assert is a (reserved) identifier with no special meaning; in particular it doesn't perform a static assertion. I get the same errors in an example by manually adding #define static_assert _Static_assert, and similar ones if I do #define static_assert foobar.

    This macro should be in <assert.h> when compiling as C, but a properly written <assert.h> should wrap it in #ifndef __cplusplus so that it doesn't apply to C++ sources. So I suspect either:

    • Your system's <assert.h> is broken

    • Your compiler is confused about what language it's compiling (unlikely because then namespace etc would also be syntax errors)

    • Something else in your tangle of headers (or command-line compilation options) is being naughty and causing #undef __cplusplus or something like that. If you've found the place where <assert.h> is included, you could try and track down the issue with strategic insertions of

#ifndef __cplusplus
#error Aargh
#endif

  1. Normally you shouldn't have to do anything. Properly written system headers will support C++ via appropriate #ifdefs, and work correctly when included into C++ programs.

    If you have a header from some third-party library that is written as C only and doesn't support C++, then you have some work to do to adapt it (or complain to its vendors). That's a project beyond the scope of this answer. Wrapping everything in extern "C" is a start but only a start.

use of assert and static assert functions

You can think of assertions as sanity checks. You know that some condition should be true unless you've screwed something up, so the assertion should pass. If you have indeed screwed something up, the assertion will fail and you'll be told that something is wrong. It's just there to ensure the validity of your code.

A static_assert can be used when the condition is a constant expression. This basically means that the compiler is able to evaluate the assertion before the program ever actually runs. You will be alerted that a static_assert has failed at compile-time, whereas a normal assert will only fail at run time. In your example, you could have used a static_assert, because the expressions 2+2==4 and 2+2==5 are both constant expressions.

static_asserts are useful for checking compile-time constructs such as template parameters. For example, you could assert that a given template argument T must be a POD type with something like:

static_assert(std::is_pod<T>::value, "T must be a POD type");

Note that you generally only want run-time assertions to be checked during debugging, so you can disable assert by #defineing NDEBUG.

C static_assert first parameter

A static_assert happens at compile time, but the decision in an if statement happens at run time. The compiler cannot know that the if clause will be false, and it needs to look at the body of the if statement to know what to do if it isn’t. It sees a static assert, which fails, so generates a compilation error.

static_assert in production code header: bad for compilation time?

If I keep the static_assert in the production code header, will they be run for every compilation unit I include the header in?

Yes. An include does nothing else then inserting the content of the included file into the including file. To the C++ compiler there's no difference whether your code comes from a header or not. The static_assert will therefore be always evaluated.

Or is the compiler somehow 'smart' about static_assert statements?

It could be. But most compilers are probably not very smart about it, since the focus of compiler development has traditionally been fast executables, not evaluating programs fast at compilation-time.

But what about performance (compilation time) if we had lots of tests?

Well, the compiler will have to execute your code. This will probably be slower than compiling and running the respective code in absolute time, but asymptotically as fast.

You maybe interested in this discussion: https://lists.llvm.org/pipermail/cfe-dev/2019-July/062799.html

As a practical recommendation: Since you are apparently using static_asserts to unit test your code, I would recommend to remove them from the header. Tests should always be separated from the tested code.

Static assert in C

C11 standard adds the _Static_assert keyword.

This is implemented since gcc-4.6:

_Static_assert (0, "assert1"); /* { dg-error "static assertion failed: \"assert1\"" } */

The first slot needs to be an integral constant expression. The second slot is a constant string literal which can be long (_Static_assert(0, L"assertion of doom!")).

I should note that this is also implemented in recent versions of clang.

Use static_assert to ensure template parameter is only used *at most* once

You can solve this problem with a macro:

#define GPIO(Port, Pin) \
friend void _gpio_ ## Port ## _ ## Pin(){} \
Gpio<Port, Pin>

Then if you use GPIO(2, 2) twice, the compiler will say something like this:

error: redefinition of '_gpio_2_2'
GPIO(2, 2) ledX;
^

<source>:14:3: note: previous definition is here
GPIO(2, 2) ledB;
^

Demo: https://godbolt.org/z/ronV0u

How to do static_assert with macros?

Prior to C++11 I would normally do:

typedef int static_assert_something[something ? 1 : -1];

You can also look at boost static assert. But it is too bloated for my liking. It is easy to make things bigger, it is hard to make them any better.

Improving diagnostics with the help of static_assert

I agree with David Rodríguez - dribeas and in defense of compiler writers consider this example:

#include <type_traits>

class A {};

// I want the nice error message below in several functions.
// Instead of repeating myself, let's put it in a function.
template <typename U>
void check() {
static_assert(std::is_convertible<U*, const volatile A*>::value,
"U doesn't derive publicly from A "
"(did you forget to include it's header file?)");
}

template <typename U>
void f(U* u) {
// check legality (with a nice error message)
check<U>();
// before trying a failing initialization:
A* p = u;
}

class B; // I forget to include "B.h"

int main() {
B* b = nullptr;
f(b);
}

When the instantiation of f<B> starts the compiler (or the compiler writter) might think: "Humm... I need to instantiate check<U> and people always complain that compiling templates is too slow. So I'll keep going and perhaps there's something wrong below and I don't event need to instantiate check."

I believe the reasoning above makes sense. (Notice that I'm not a compiler writter so I'm just speculating here).

Both GCC 4.8 and VS2010 keep compiling f<B>, postponing the instantiation of check<B> for later. Then they find the failing initialization and provide their own error messages. VS2010 stops immediately and I don't get my nice error message! GCC keeps going and yields the message that I wanted (but only after its own).

Metaprogramming is tricky for the programmers and for the compilers. static_assert helps a lot but it's not a panacea.



Related Topics



Leave a reply



Submit