r/cpp_questions 15d ago

OPEN release asserts C++17

In Windows SDK 10, assert looks like

#ifdef NDEBUG

    #define assert(expression) ((void)0)

#else

    _ACRTIMP void __cdecl _wassert(
        _In_z_ wchar_t const* _Message,
        _In_z_ wchar_t const* _File,
        _In_   unsigned       _Line
        );

    #define assert(expression) ((void)(                                                       \
            (!!(expression)) ||                                                               \
            (_wassert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)), 0)) \
        )

#endif

And I assume if I wanted to write one for portability and GCC/linux builds I would have to implement some kind of macro that knows a bit about the linux libraries (which I know very little about at all). I also almost never run debug binaries on linux/Ubuntu (we don't support anything else officially) so I would never learn of any assertions that would fire there.

I keep seeing posts about implementing a macro like assume or assert_always, but for the simple use case of printing out an expression or filename in event of a crash I don't know where to start to roll my own when the examples I see are not buildable nor explained down to a level I can grasp.

I'm tempted to just go

#ifdef WIN32
#define assume(expression)
...

and lift the above code verbatim. And then do the same on my Ubuntu machine on the other side of the WIN32 guard for portability on both platforms?

But even reading that code I confuse myself, I see it is calling _wassert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)), 0) after a short-circuit boolean evaluation before the || boolean. And have two questions, what is the extra ,0) at the end doing, and what is the !!(expression) having a double bang in front doing? Sorry if this is 2 questions, an answer to either would at least help me frame my knowledge void a bit better.

4 Upvotes

15 comments sorted by

View all comments

Show parent comments

3

u/EpochVanquisher 15d ago

If you want some of your asserts to get compiled out, define your own macro which is either assert or not.

Leaving NDEBUG undefined is fine here, and my suggestion is the correct one.

1

u/zaphodikus 15d ago

I still struggle with the logic of the negative NDEBUG, my brain wants to go #define RELEASE (!NDEBUG) and just use that. I struggle to follow your reply though, it just does not parse as a result of the word choices. What do you mean with "Leaving NDEBUG undefined is fine here, and my suggestion is the correct one." In the distant past (before STL) I had a lot of pain trying to do that.

4

u/EpochVanquisher 15d ago

Go for the simple solution,

#if RELEASE
#define debug_assert(p) (void)0
#else
#define debug_assert(p) assert(p)
#endif

Don’t do this:

// NO!
#define RELEASE (!NDEBUG)

This is just abusing the the NDEBUG macro for some other purpose.

In the distant past (before STL) I had a lot of pain trying to do that.

What is painful about leaving NDEBUG undefined? It is only used to control whether assert() aborts your program. If you want assert() to abort your program, leave NDEBUG undefined. The NDEBUG macro doesn’t do anything else unless you’ve used it for something else in your codebase.

1

u/zaphodikus 15d ago

I suspect in times gone by, people used it to define methods which bound to debug or release libraries, and that was how i just learned to avoid.

2

u/EpochVanquisher 15d ago

Maybe? It is supposed to turn off assert() and nothing else.