Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

"GotW #97: Assertions (Difficulty: 4/10)" by Herb Sutter

72 views
Skip to first unread message

Lynn McGuire

unread,
Jan 4, 2021, 8:23:43 PM1/4/21
to
"GotW #97: Assertions (Difficulty: 4/10)" by Herb Sutter

https://herbsutter.com/2021/01/01/gotw-97-contracts-part-1-assertions-and-postconditions/

Wow, we do not have any asserts in our 450,000 lines of C++ code.

Lynn

Paavo Helde

unread,
Jan 5, 2021, 4:14:49 AM1/5/21
to
For comparison, we have 7708 asserts in 600,000 lines of C++ code. I
guess this depends on the project type very much.

spudisno...@grumpysods.com

unread,
Jan 5, 2021, 4:27:33 AM1/5/21
to
Presumably in the production version they're disabled by a build option?

Bonita Montero

unread,
Jan 5, 2021, 4:58:50 AM1/5/21
to
I've got my own assert-/assume-header:

#pragma once
#include <cassert>

#if !defined(assume)
#if defined(_MSC_VER)
#define assume(e) (__assume(e))
#elif defined(__GNUC__)
#define assume(e) \
{ \
if( !(e) ) \
__builtin_unreachable(); \
}
#else
#define assume(e) ((void)0)
#endif
#endif

#if !defined(xassert)
#if !defined(NDEBUG)
#define xassert(e) (assert(e))
#else
#if defined(_MSC_VER)
#define xassert(e) assume(e)
#elif defined(__GNUC__)
#define xassert(e) \
{ \
if( !(e) ) \
__builtin_unreachable(); \
}
#else
#define xassert(e) ((void)0)
#endif
#endif
#endif

spudisno...@grumpysods.com

unread,
Jan 5, 2021, 5:04:16 AM1/5/21
to
On Tue, 5 Jan 2021 10:58:34 +0100
Bonita Montero <Bonita....@gmail.com> wrote:
>I've got my own assert-/assume-header:
>
>#pragma once
>#include <cassert>
>
>#if !defined(assume)
> #if defined(_MSC_VER)
> #define assume(e) (__assume(e))

I've never come across assume(). What does it do? Google isn't clear.

David Brown

unread,
Jan 5, 2021, 6:21:24 AM1/5/21
to
It's a macro that Bonita has defined.

I use an "assume" macro too, with a similar definition (this is for
gcc-compatible code, so I don't bother checking for __GNUC__) :


// Non-existent function called if it is known at compile-time that an
// assume will fail
extern void __attribute__((error("Assume failed"))) assumeFailed(void);

// The compiler can assume that "x" is true, and optimise or warn
// accordingly
// If the compiler can see that the assume will fail, it gives an error
#define assume(x) \
do { \
if (__builtin_constant_p(x)) { \
if (!(x)) { \
assumeFailed(); \
} \
} \
if (!(x)) __builtin_unreachable(); \
} while (0)


Basically, I use "assume" to tell the compiler some fact that I think it
will find useful for optimisation, but without doing any run-time tests
to check it. (But with my version, if the compiler can see at
compile-time that the assumption is wrong, it will give a compile-time
error - a little extra safety.) It would also be reasonable to have a
compile-time option to enable run-time check of the assumptions, as an
aid to debugging.

Paavo Helde

unread,
Jan 5, 2021, 6:25:38 AM1/5/21
to
Yes, in release builds these are disabled, both for speed and for
avoiding spurious false alarms (when you write thousands of asserts,
some of those are bound to be buggy (i.e. over-cautious)).

ijw wij

unread,
Jan 5, 2021, 8:45:54 AM1/5/21
to
> >
> > Presumably in the production version they're disabled by a build option?
> Yes, in release builds these are disabled, both for speed and for
> avoiding spurious false alarms (when you write thousands of asserts,
> some of those are bound to be buggy (i.e. over-cautious)).

Funny!

Öö Tiib

unread,
Jan 5, 2021, 5:47:47 PM1/5/21
to
No, it is just human nature. We are all ignorant about some aspect,
misremembering, absent minded, tired and fallible. We just learn,
memorize, recall, concentrate our focus and correct if we notice
as we go. When dealing with code base of hundreds thousands
of lines it is important to let every line you altered to be validated
by other people as it will catch most issues earlier.



Paavo Helde

unread,
Jan 6, 2021, 3:19:41 AM1/6/21
to
06.01.2021 00:47 Öö Tiib kirjutas:
> On Tuesday, 5 January 2021 at 15:45:54 UTC+2, ijw wij wrote:
>>>>
>>>> Presumably in the production version they're disabled by a build option?
>>> Yes, in release builds these are disabled, both for speed and for
>>> avoiding spurious false alarms (when you write thousands of asserts,
>>> some of those are bound to be buggy (i.e. over-cautious)).
>> Funny!
>
> No, it is just human nature. We are all ignorant about some aspect,
> misremembering, absent minded, tired and fallible. We just learn,
> memorize, recall, concentrate our focus and correct if we notice
> as we go.

Agreed.

Here we are talking about assert lines which are themselves meant for
validating other code. If I leave them in in the production, then
someone would need to take extra care of validating of validations.

> When dealing with code base of hundreds thousands
> of lines it is important to let every line you altered to be validated
> by other people as it will catch most issues earlier.

In my experience, no. Validating my code by myself by writing assert
lines has detected much more bugs in my code than code review by other
people.

This probably also depends on the project type. Imagine implementing
something like JPEG compression algorithm, what are the chances a human
reviewer would notice an one-off mistake somewhere deep in the algorithm?

Jorgen Grahn

unread,
Jan 6, 2021, 4:26:39 AM1/6/21
to
For the record, I'm against ever disabling asserts, and I'm against
debug/release builds in general. But there are different schools of
thought. Most people at my workplace agree with Paavo.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

spudisno...@grumpysods.com

unread,
Jan 6, 2021, 4:37:15 AM1/6/21
to
On 6 Jan 2021 09:26:20 GMT
There is often no right answer. Would you want the avionic software in an
aircraft to assert and die at 30K feet possibly rendering the aircraft
uncontrollable, or would you want it to continue running with bad data
and possibly put the aircraft into an unrecoverable dive? Hello Boeing!

Jorgen Grahn

unread,
Jan 6, 2021, 7:42:00 AM1/6/21
to
I don't intend to join yet another long discussion about assertions,
but just to provide one other viewpoint:

If I have an 'assert(expr)' in that code, it means

"When I wrote this code I assumed expr would be true at this
point. If it's not, it means I made a horrible design error,
and I have no idea what the code will do from now on. Whatever it
is, it's nothing that has ever been tested or thought of."

and then, yes, I would prefer the software to die at 30K feet!
Provided that there is a fallback ... Which there probably needs to
be, anyway.

I don't see assertions as very different from other things which lead
to crashes: segfaults, division by zero ... they all protect me from
the craziness which would otherwise follow. The main difference is I
have to add the assertions manually.

Alf P. Steinbach

unread,
Jan 6, 2021, 9:52:12 AM1/6/21
to
Consider classifying asserts based on computational cost, and disabling
only the very costly (e.g. O(n) time check) asserts in releases.

There is a difference between e.g. jet fighter flight control software
that should just keep on working no matter what, and say a billing
system. Which can greatly influence the choice of leaving fast
assertions enabled or not. But e.g. for the latter, it seems to me
better that the system crashes than that it produces incorrect bills.

- Alf

Manfred

unread,
Jan 6, 2021, 10:52:40 AM1/6/21
to
On 1/6/21 9:19 AM, Paavo Helde wrote:
> 06.01.2021 00:47 Öö Tiib kirjutas:
>> On Tuesday, 5 January 2021 at 15:45:54 UTC+2, ijw wij wrote:
>>>>>
>>>>> Presumably in the production version they're disabled by a build
>>>>> option?
>>>> Yes, in release builds these are disabled, both for speed and for
>>>> avoiding spurious false alarms (when you write thousands of asserts,
>>>> some of those are bound to be buggy (i.e. over-cautious)).
>>> Funny!
>>
>> No, it is just human nature. We are all ignorant about some aspect,
>> misremembering, absent minded, tired and fallible. We just learn,
>> memorize, recall, concentrate our focus and correct if we notice
>> as we go.
>
> Agreed.
>
> Here we are talking about assert lines which are themselves meant for
> validating other code. If I leave them in in the production, then
> someone would need to take extra care of validating of validations.

Agreed on the agreement :)
I like the "validate the validation" part.

>
>> When dealing with code base of hundreds thousands
>> of lines it is important  to let every line you altered to be validated
>> by other people as it will catch most issues earlier.
>
> In my experience, no. Validating my code by myself by writing assert
> lines has detected much more bugs in my code than code review by other
> people.

I am also a fan of asserts - I tend to spread them around a lot, and I
agree that as far as debugging goes they are probably more effective
than code reviews - probably related with my programming habits.

Nonetheless code reviews do have their value, especially with respect to
code maintainability - some extra pairs of eyes /can/ make code more
clear and readable. They can occasionally spot mistakes, if the
reviewers are member of the same team that share knowledge about the
problem domain.

Reviews in general, however, are /very/ useful, especially when working
in team.

>
> This probably also depends on the project type. Imagine implementing
> something like JPEG compression algorithm, what are the chances a human
> reviewer would notice an one-off mistake somewhere deep in the algorithm?

This is also true, the problem domain plays a significant role in
asserts' effectiveness. I guess because of how it impacts code structure.

Manfred

unread,
Jan 6, 2021, 11:08:51 AM1/6/21
to
On 1/6/21 3:51 PM, Alf P. Steinbach wrote:
> On 05.01.2021 12:25, Paavo Helde wrote:
>> 05.01.2021 11:27 spudisno...@grumpysods.com kirjutas:
>>> On Tue, 5 Jan 2021 11:14:29 +0200
>>> Paavo Helde <myfir...@osa.pri.ee> wrote:
>>>> 05.01.2021 03:23 Lynn McGuire kirjutas:
>>>>> "GotW #97: Assertions (Difficulty: 4/10)" by Herb Sutter
>>>>>
>>>>>
>>>> https://herbsutter.com/2021/01/01/gotw-97-contracts-part-1-assertions-and-postc
>>>>
>>>> onditions/
>>>>>
>>>>>
>>>>> Wow, we do not have any asserts in our 450,000 lines of C++ code.
>>>>
>>>> For comparison, we have 7708 asserts in 600,000 lines of C++ code. I
>>>> guess this depends on the project type very much.
>>>
>>> Presumably in the production version they're disabled by a build option?
>>
>> Yes, in release builds these are disabled, both for speed and for
>> avoiding spurious false alarms (when you write thousands of asserts,
>> some of those are bound to be buggy (i.e. over-cautious)).
>
> Consider classifying asserts based on computational cost, and disabling
> only the very costly (e.g. O(n) time check) asserts in releases.

Hmm. I don't think it depends on performance - I mean the choice whether
to leave an assert in place or not.

>
> There is a difference between e.g. jet fighter flight control software
> that should just keep on working no matter what, and say a billing
> system. Which can greatly influence the choice of leaving fast
> assertions enabled or not. But e.g. for the latter, it seems to me
> better that the system crashes than that it produces incorrect bills.

This is indeed a good example (unrelated with computational cost btw.)
However I doubt if 'assert' is a good name for some piece of code that
detects an error in billing, which is supposed to abort a transaction.
It probably falls into the category of software exception; asserts are
supposed to be strictly tested before release.
I mean, you sure verify if an assert triggers during testing. If you
want to code some consistency check to be executed as part of the
released product, most probably you should choose some other construct
than 'assert'.

>
> - Alf

Paavo Helde

unread,
Jan 6, 2021, 1:30:13 PM1/6/21
to
To be honest, I would not trust myself to write jet fighter software,
I'm too impatient for that. Fortunately our software is much more
mundane and if it crashes or produces wrong results, nobody dies (at
least not directly).

On the other hand, performance is critical for our software and enabling
all asserts in production is not an option. Actually we have a second
flavor of assert which is also present in release builds, but this is
used very sparingly because the resulting error messages would be "too
technical" for our customers. Instead, there are lots of condition
checks and explicit exception throwing, most of which are never
triggered in production.

Makis

unread,
Jan 6, 2021, 1:38:51 PM1/6/21
to
The maxim should be: Don't die on assert! In any software!

Jorgen Grahn

unread,
Jan 6, 2021, 1:48:01 PM1/6/21
to
On Wed, 2021-01-06, Manfred wrote:
> On 1/6/21 3:51 PM, Alf P. Steinbach wrote:
...

>> There is a difference between e.g. jet fighter flight control software
>> that should just keep on working no matter what, and say a billing
>> system. Which can greatly influence the choice of leaving fast
>> assertions enabled or not. But e.g. for the latter, it seems to me
>> better that the system crashes than that it produces incorrect bills.
>
> This is indeed a good example (unrelated with computational cost btw.)
> However I doubt if 'assert' is a good name for some piece of code that
> detects an error in billing, which is supposed to abort a transaction.
> It probably falls into the category of software exception; asserts are
> supposed to be strictly tested before release.

Are you talking about the assert() macro specifically? I think a lot
of people use it in a wider sense, so that "an assert" which fails
could e.g. log and abort an transaction. In fact I think Stroustrup
uses it that way in TC++PL, when he recommends not using assert().

The most important part of an assert is IMO that it establishes a
precondition or invariant -- not exactly what happens if it does not
hold.

> I mean, you sure verify if an assert triggers during testing. If you
> want to code some consistency check to be executed as part of the
> released product, most probably you should choose some other construct
> than 'assert'.

Alf didn't write about any consistency checks. Note that
preconditions and invariants can be found in any software system.

Manfred

unread,
Jan 6, 2021, 2:34:58 PM1/6/21
to
On 1/6/21 7:47 PM, Jorgen Grahn wrote:
> On Wed, 2021-01-06, Manfred wrote:
>> On 1/6/21 3:51 PM, Alf P. Steinbach wrote:
> ...
>
>>> There is a difference between e.g. jet fighter flight control software
>>> that should just keep on working no matter what, and say a billing
>>> system. Which can greatly influence the choice of leaving fast
>>> assertions enabled or not. But e.g. for the latter, it seems to me
>>> better that the system crashes than that it produces incorrect bills.
>>
>> This is indeed a good example (unrelated with computational cost btw.)
>> However I doubt if 'assert' is a good name for some piece of code that
>> detects an error in billing, which is supposed to abort a transaction.
>> It probably falls into the category of software exception; asserts are
>> supposed to be strictly tested before release.
>
> Are you talking about the assert() macro specifically?

The discussion upthread is explicitly about asserts that result in
process termination if the test is enabled and fails, and may or may
not be disabled in production code. This fits the assert() macro
specifically, or any other equivalent.

I think a lot
> of people use it in a wider sense, so that "an assert" which fails
> could e.g. log and abort an transaction. In fact I think Stroustrup
> uses it that way in TC++PL, when he recommends not using assert().

I'm OK with a feature (function or macro) which asserts in a broader
sense, however, given that assert() is so well established and known, I
think that it is confusing to call 'assert' something that is non-fatal.

>
> The most important part of an assert is IMO that it establishes a
> precondition or invariant -- not exactly what happens if it does not
> hold.

I guess you mean if it is not fatal? 'Cause that's what this is all about.

>
>> I mean, you sure verify if an assert triggers during testing. If you
>> want to code some consistency check to be executed as part of the
>> released product, most probably you should choose some other construct
>> than 'assert'.
>
> Alf didn't write about any consistency checks. Note that
> preconditions and invariants can be found in any software system.

Maybe preconditions and invariants would be a better term - semantics.
That's not the point.

My point is that if you want such kind of test delivered in the released
product, use something else than assert(). That's better than using
assert() and #undef NDEBUG


>
> /Jorgen
>

Ian Collins

unread,
Jan 6, 2021, 2:45:56 PM1/6/21
to
I share your views and workplace battles...


If asserts are used correctly, they are there to protect against
unrecoverable errors, so a controlled exit with a nice stack dump and
core is the only safe course of action.

If asserts are disabled, how does the code know it is safe to proceed?

--
Ian.

Scott Lurndal

unread,
Jan 6, 2021, 4:35:23 PM1/6/21
to
Asserts are horribly user-unfriendly.

Detect the issue, sure, but rather than the unfriendly and deadly
assertion, one can

1) Correct the error and continue.
2) Report the error and reset state (e.g. an interactive command interpreter)
3) In library code, report the error to the caller though an error code (e.g. errno).

We're quite careful to ensure that our customers never see an assertion trigger.

static_assert is quite useful, however, in C++ code.

Ian Collins

unread,
Jan 6, 2021, 5:06:54 PM1/6/21
to
I agree.

> Detect the issue, sure, but rather than the unfriendly and deadly
> assertion, one can
>
> 1) Correct the error and continue.

Don't use asserts where this is possible. Asserts are like exceptions
on steroids. Use exceptions for exceptional conditions, asserts for
impossible conditions...

> 2) Report the error and reset state (e.g. an interactive command interpreter)

That's effectively what we do (ours is an embedded controller).

> 3) In library code, report the error to the caller though an error code (e.g. errno).

Yes, asserts in library code are a bad idea.

> We're quite careful to ensure that our customers never see an assertion trigger.

Ours will see brief reset.

> static_assert is quite useful, however, in C++ code.

Very useful when writing cross-platform code.

--
Ian.

Lynn McGuire

unread,
Jan 6, 2021, 6:26:54 PM1/6/21
to
We put in extensive tests that run both in debug and release modes.

BTW, my software also has 850,000 lines of Fortran code also for a total
of 1.3 million lines of F77 and C++.

Lynn

David Brown

unread,
Jan 7, 2021, 3:42:50 AM1/7/21
to
On 06/01/2021 22:35, Scott Lurndal wrote:

>
> static_assert is quite useful, however, in C++ code.
>

I'd go further and say static assertions are extremely useful in C and
C++ code, and have been so from long before they were added to the
languages. (Prior to C11/C++11 they could be done with some ugly macros.)

They have no costs at run-time, serve to document assumptions or
requirements, and make code more robust. As has been noted in this
thread, run-time assertions have run-time costs and open questions such
as what to do when the assertion fails, and how you test the assertion
(untested error handling code is not a good idea) - static assertions
have no such complications.

Ian Collins

unread,
Jan 7, 2021, 4:21:52 AM1/7/21
to
An effective way to test assertion failures is to have them throw an
exception when running unit tests.

--
Ian.

David Brown

unread,
Jan 7, 2021, 4:37:29 AM1/7/21
to
But if those assertions are still in the final code, configured to do
something else (log an error, restart the system, or whatever), then you
are not testing the actual code.

Many failures in real systems have been caused by people putting in
extra checks "just to be sure" without proper verification of how those
checks work and what happens if they trigger.

Paavo Helde

unread,
Jan 7, 2021, 7:35:18 AM1/7/21
to
07.01.2021 11:37 David Brown kirjutas:
> On 07/01/2021 10:21, Ian Collins wrote:
>> On 07/01/2021 21:42, David Brown wrote:
>>> On 06/01/2021 22:35, Scott Lurndal wrote:
>>>
>>>>
>>>> static_assert is quite useful, however, in C++ code.
>>>>
>>>
>>> I'd go further and say static assertions are extremely useful in C and
>>> C++ code, and have been so from long before they were added to the
>>> languages.  (Prior to C11/C++11 they could be done with some ugly
>>> macros.)
>>>
>>> They have no costs at run-time, serve to document assumptions or
>>> requirements, and make code more robust.  As has been noted in this
>>> thread, run-time assertions have run-time costs and open questions such
>>> as what to do when the assertion fails, and how you test the assertion
>>> (untested error handling code is not a good idea) - static assertions
>>> have no such complications.
>>
>> An effective way to test assertion failures is to have them throw an
>> exception when running unit tests.
>>
>
> But if those assertions are still in the final code, configured to do
> something else (log an error, restart the system, or whatever), then you
> are not testing the actual code.

Over the years I have made up my mind about assertions: these are just
development and debugging aids and should not be present in released code.

The same holds for other assert-like features like MSVC checked
iterators. Even MS have figured it out by now, see
"https://docs.microsoft.com/en-us/cpp/standard-library/checked-iterators?view=msvc-160"
:

"By default, the value for _ITERATOR_DEBUG_LEVEL is 0 for release builds
and 2 for debug builds."

YMMV, of course.

>
> Many failures in real systems have been caused by people putting in
> extra checks "just to be sure" without proper verification of how those
> checks work and what happens if they trigger.

Agreed.


James Kuyper

unread,
Jan 7, 2021, 9:24:02 AM1/7/21
to
On 1/7/21 7:35 AM, Paavo Helde wrote:
...
> Over the years I have made up my mind about assertions: these are just
> development and debugging aids and should not be present in released code.

If you have such aids disabled in the released code, the code should be
thoroughly tested with those aids disabled, before being released.

Paavo Helde

unread,
Jan 7, 2021, 10:37:23 AM1/7/21
to
Not sure if you meant s/disabled/enabled/ in the last line. With
"enabled" I agree of course, with "disabled" not.

A non-op is a non-op, and all such aids must be non-ops if not triggered
(and fixed ASAP when triggered). If some developer writes non-op asserts
then it's a disciplinary problem, not a technical one.

Paavo Helde

unread,
Jan 7, 2021, 10:39:36 AM1/7/21
to
07.01.2021 17:37 Paavo Helde kirjutas:
> If some developer writes non-op asserts
> then it's a disciplinary problem, not a technical one.

Heh, wrote the opposite myself here, please s/non-op/non-non-op/ ;-)

James Kuyper

unread,
Jan 7, 2021, 10:54:00 AM1/7/21
to
On 1/7/21 10:37 AM, Paavo Helde wrote:
> 07.01.2021 16:23 James Kuyper kirjutas:
>> On 1/7/21 7:35 AM, Paavo Helde wrote:
>> ...
>>> Over the years I have made up my mind about assertions: these are just
>>> development and debugging aids and should not be present in released code.
>>
>> If you have such aids disabled in the released code, the code should be
>> thoroughly tested with those aids disabled, before being released.
>
> Not sure if you meant s/disabled/enabled/ in the last line. With
> "enabled" I agree of course, with "disabled" not.

No, I meant disabled. You need to test the code as delivered, it may
malfunction in ways that are different from the way it functions when
those aids are enabled.

> A non-op is a non-op, and all such aids must be non-ops if not triggered
> (and fixed ASAP when triggered).

Those aids should be no-ops when disabled - but assuming that they
actually are no-ops is a bad idea. Among other things, their absence may
affect the way that other code is optimized.

Richard Damon

unread,
Jan 7, 2021, 10:56:52 AM1/7/21
to
On 1/7/21 10:37 AM, Paavo Helde wrote:
The key is that if you had the tests in place for development, you may
run into an issue that removing the tests break the code. This can
happen if the test have some unnoticed side effect, that omitting
creates a problem.

wij

unread,
Jan 7, 2021, 11:02:44 AM1/7/21
to
If broken down:

#define ASSERT(expr) \ // 1.executable comment (run-time check)
if(!expr) { \ //
cerr << #expr; \ // 2.print expr
abort(); \ // 3.abort
}

I rarely use assert because of (2) and (3).
If I am to implement a complex algorithm like JPEG compression, I would
write something like:
if(cond==false) {
THROW(-1) // register __FILE__, __LINE__ and throw a general error
}
But, in general, such codes are not common ( implement complex algorithm is
not a common task)

Paavo Helde

unread,
Jan 7, 2021, 1:37:48 PM1/7/21
to
Just compiling in release mode will affect how the code is optimized, no
need to bring debug asserts into play.

And of course the final code needs to be tested (and even more than
debug builds), it's just that removal of asserts is not a major or even
a minor reason for that.

Of course, disabling an assert may hide or obfuscate an existing bug,
but I have never seen a bug *created* by disabling an assert. On the
other hand I have seen multiple problems caused by over-eager asserts
left in the code. I believe my rants about MSVC checked iterators can
still be found in ancient c.l.c++.m archives.

Paavo Helde

unread,
Jan 7, 2021, 2:00:59 PM1/7/21
to
The exact definition of the assert macro is not so important because one
can always run the code in the debugger and put a breakpoint in the
failure branch. But yes, our asserts throw exceptions instead of abort()
as these are easier to manage.

If asserts are disabled in release, then they have zero cost and one can
freely use them also in non-complex code, to protect against typos,
copy-paste errors, etc. For example, just today I wrote:

std::string src = /*...*/;
// add zero padding; leave room for 16-byte MD5 hash.
unsigned int k = (src.length()+16) % enc.MandatoryBlockSize();
if (k!=0) {
k = enc.MandatoryBlockSize() - k;
}
src.resize(src.length()+16+k);
DEBUG_ASSERT(src.length() >= 16);
DEBUG_ASSERT(src.length() % enc.MandatoryBlockSize() == 0);

Silly asserts (esp the 1st), but I feel more confident I have not
mistyped anything, and that if I change something in the future there
are less chances I screw it up. The asserts also document the needed
preconditions for next lines.


Ian Collins

unread,
Jan 7, 2021, 2:28:04 PM1/7/21
to
On 07/01/2021 22:37, David Brown wrote:
> On 07/01/2021 10:21, Ian Collins wrote:
>> On 07/01/2021 21:42, David Brown wrote:
>>> On 06/01/2021 22:35, Scott Lurndal wrote:
>>>
>>>>
>>>> static_assert is quite useful, however, in C++ code.
>>>>
>>>
>>> I'd go further and say static assertions are extremely useful in C and
>>> C++ code, and have been so from long before they were added to the
>>> languages.  (Prior to C11/C++11 they could be done with some ugly
>>> macros.)
>>>
>>> They have no costs at run-time, serve to document assumptions or
>>> requirements, and make code more robust.  As has been noted in this
>>> thread, run-time assertions have run-time costs and open questions such
>>> as what to do when the assertion fails, and how you test the assertion
>>> (untested error handling code is not a good idea) - static assertions
>>> have no such complications.
>>
>> An effective way to test assertion failures is to have them throw an
>> exception when running unit tests.
>>
>
> But if those assertions are still in the final code, configured to do
> something else (log an error, restart the system, or whatever), then you
> are not testing the actual code.

How so? You are testing the behaviour of the code when "impossible"
circumstances occur (when the expectation is an assert), not the
behaviour of the assertion mechanism. It is trivial to test the
behaviour of the assertion mechanism with a death test.

> Many failures in real systems have been caused by people putting in
> extra checks "just to be sure" without proper verification of how those
> checks work and what happens if they trigger.

Which is precisely why asserts should be unit tested. By their very
nature, real world testing can't cause an assert.

--
Ian


James Kuyper

unread,
Jan 7, 2021, 3:40:50 PM1/7/21
to
On 1/7/21 1:37 PM, Paavo Helde wrote:
...
> Of course, disabling an assert may hide or obfuscate an existing bug,
> but I have never seen a bug *created* by disabling an assert. On the

Neither have I. But that's partly because I don't use asserts very much
- the results when the assert triggers is generally not what I want to
have happen. I prefer the resulting message to contain information about
the specific values that triggered the assert, and the context of the
assert.
However, I have seen a bug hidden by enabling the constructs I use
instead of an assert. The fact that the it depended upon the value of a
given variable meant that code had to be generated to retrieve the value
of that variable. As a result, how that variable was optimized changed
in a way that hid a problem with how that variable was used. It's been a
while, so I unfortunately don't remember the details. It's possibly the
case that, since the framework I use instead of asserts is heavier than
a typical assert, it is more likely to affect how the code is optimized.

Scott Lurndal

unread,
Jan 7, 2021, 4:11:45 PM1/7/21
to
James Kuyper <james...@alumni.caltech.edu> writes:
>On 1/7/21 1:37 PM, Paavo Helde wrote:
>...
>> Of course, disabling an assert may hide or obfuscate an existing bug,
>> but I have never seen a bug *created* by disabling an assert. On the
>
>Neither have I. But that's partly because I don't use asserts very much
>- the results when the assert triggers is generally not what I want to
>have happen. I prefer the resulting message to contain information about
>the specific values that triggered the assert, and the context of the
>assert.

I've seen them. Anytime there's a side effect in the assert, the behavior
will change when -DNDEBUG is specified. These can be very obscure and
difficult to find.

Richard Damon

unread,
Jan 7, 2021, 6:26:21 PM1/7/21
to
It is especially easy if you have a function call that you use as part
of the condition, that has side effects that you don't think about.

Juha Nieminen

unread,
Jan 8, 2021, 3:31:16 AM1/8/21
to
Paavo Helde <myfir...@osa.pri.ee> wrote:
> If asserts are disabled in release, then they have zero cost and one can
> freely use them also in non-complex code, to protect against typos,
> copy-paste errors, etc. For example, just today I wrote:
>
> std::string src = /*...*/;
> // add zero padding; leave room for 16-byte MD5 hash.
> unsigned int k = (src.length()+16) % enc.MandatoryBlockSize();
> if (k!=0) {
> k = enc.MandatoryBlockSize() - k;
> }
> src.resize(src.length()+16+k);
> DEBUG_ASSERT(src.length() >= 16);
> DEBUG_ASSERT(src.length() % enc.MandatoryBlockSize() == 0);
>
> Silly asserts (esp the 1st), but I feel more confident I have not
> mistyped anything, and that if I change something in the future there
> are less chances I screw it up. The asserts also document the needed
> preconditions for next lines.

One could also argue that unit tests do the same thing as asserts,
and are better in this regard.

Ian Collins

unread,
Jan 8, 2021, 5:06:52 AM1/8/21
to
Much better!

--
Ian.

Vir Campestris

unread,
Jan 8, 2021, 5:02:33 PM1/8/21
to
On 06/01/2021 14:51, Alf P. Steinbach wrote:
> There is a difference between e.g. jet fighter flight control software
> that should just keep on working no matter what, and say a billing
> system. Which can greatly influence the choice of leaving fast
> assertions enabled or not. But e.g. for the latter, it seems to me
> better that the system crashes than that it produces incorrect bills.

Our systems aren't safety critical. We have asserts in place, which
result in a crash dump being sent back for us to look at.

They don't happen often; we caught all the obvious bugs in testing.

But they do happen, and we can use the dumps, and the logs we collect,
to make sure they don't happen again. Or at worst have some clue as to
what checks to add to the next release.

Andy
0 new messages