Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Helpful assert errors #240

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

Helpful assert errors #240

wants to merge 1 commit into from

Conversation

spgarbet
Copy link

Original at vubiostat/r-yaml#22

Per Tim Hesterberg:

In C, the assert macro takes a boolean value, or something that can convert to a boolean value. At run time, if this value is true, nothing happens and the program continues. On false, an assertion is raised, the program crashes, and the line with the assert is printed out. So an assertion written:

assert(size > 0);

Would get something like this when the assertion is triggered:

Assertion failed: 'size > 0'

Probably with more information like file name, line number, etc. However, there is no second argument to assert to specify why the assert exists. To do that, a string literal can be inserted into the boolean argument like so:

assert(size > 0 && "need at least one element");
Assertion failed: 'size > 0 && "need at least one element"'

The string literal has the type of a character array. Arrays can decay to a pointer to the element type. Pointers can convert to boolean value, with non-null pointers becoming a true value. Arrays that decay to pointers always decay to non-null pointers. This means that string literals in a boolean context evaluate to true. By boolean logic, (X && true) == (X), so it is safe to append '&& "message"' to any boolean expression without changing its value.

If there is a code path that should never be reached, an assertion that always fails should be inserted. The smallest such form is:

assert(0);

Then, if a message is required:

assert(0 && "oops");

Both the arguments evaluate to false and the assertion always triggers. However, a common mistake with asserts is to omit the '0 &&' and write:

assert("oops");

With this mistake, "oops" always evaluated to true, the assertion never triggers, and this code branch is no longer protected. I think it's a good idea to pass on to the package maintainer so that future versions will not have this problem.

@i-ky
Copy link

i-ky commented Feb 15, 2022

I don't think that adding string literals into assert() conditions is a good idea. Compilers know that string literals are not NULL, that X && true == X and that && true is unneeded and, therefore, suspicious. I guess that compilers may issue a warning in such case.

I don't know the exact reason, but cppreference.com recommends a different way of incorporating messages into assert(), namely:

assert(("There are five lights", 2 + 2 == 5));

In any case, if additional information is desired on assert() failures, I would recommend adding a custom macro, e.g.:

#define assert_with_reason(expression, reason) assert((reason, expression))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants