Date: Mon, 25 Aug 2025 02:51:09 -0700
"I wouldn't want macros in the standard which behave differently based
on build types"
It sounds like we have different points of view and I understand I am
contrarian on this one. But I have never heard a story about someone
having a serious problem with multiple builds this way. Surely there
was a really good story once because word seems to have gotten around.
However, for me this is just normal stuff when you work at company
that cross compiles to a number of different resource constrained
targets. I recently wrote scripts that test 20 different builds for
one code base and that seems to be a great way to write really correct
code.
If I could require assertion checks not to have side-effects it should
be safe to change the meaning of that code from build to build.
I fell out of love with printf too. Until I realized it was actually
an op-code interpreter with a validation tool available for it. But
I'll use whatever works best. That assert code I posted was designed
to work in plain C too and not just C++ which might matter in some
code bases.
The assertion macro also calls functions that can't be constexpr,
which I had thought precluded use in a manifestly const context. That
might be something I need to look at more closely, I'll admit my last
attempt at using "if constexpr" for that just refused to compile as
expected and I decided to go do something else.
Sorry I forgot about breakpoint being added. Thank you for taking the
time to respond.
From: Jan Schultke <janschultke_at_[hidden]>
Date: Mon, 25 Aug 2025 06:46:11 +0200
> I would argue there has to be support by default for making the debugger
> stop on the exact line where > the assert fired.
> There is just no excuse for having a software engineer do unnecessary manual labour
> every time an assertion fires.
Well, we have std::breakpoint() and std::breakpoint_if_debugging()
now, so that seems like what you need to make it happen.
> There are also uses for having different "levels" of severity when it comes to asserts.
"Debug" and "release" builds are outside the scope of the standard
(with the weird exception of the NDEBUG macro). I wouldn't want macros
in the standard which behave differently based on build types if there
is no such thing as "build type" in the standard in the first place.
> This didn't work reliably with constexpr
> and you have to check the generated assembly to find that out.
Everything works reliably with constexpr. If you store a value in a
constexpr variable, no matter what, it will always be
constant-evaluated. What you probably mean is that your function isn't
always being inlined and optimized away, but so is life. That can
happen with compilers.
> Next is the issue of formatting assert messages.
> It turns out to be really handy to have debug asserts
> that take C-style variadic arguments and perform printf formatting.
> There is an issue here with having untested format strings and that does need to be resolved. However, by leveraging gcc's ability to check a printf format string against its arguments
> I was able to avoid any issues in my own work.
> This approach avoids making an unnecessary local function call
> or allocating memory after an assertion fails.
You already have all the functionality you need. You can
std::format_to with some output iterator that writes to a file
character by character. This will never allocate memory, and gives you
pretty powerful formatting for your assertion messages.
C-style variadics in the C++ standard library are a terrible idea, and
I'd strongly oppose them on the basis of safety and common sense.
> The standard library also has explicit language allowing for its asserts
> to operate in a manifestly const environment.
> It would be nice for others to be able to access that functionality
> without mucking up the callstack for every assert in the codebase.
You can just denote a function as constexpr, right? What functionality
do you think you're missing? A typical assertion macro is only going
to deepen the call stack when an assertion fails, and that seems like
a bit of a non-issue, especially if you get the "correct" breakpoint
via std::breakpoint_if_debugging first. The extra call would be
avoidable if you implemented it all via macros, but that would just
increase the code size all over the place, for assertions that aren't
ever meant to trigger (or trigger once immediately before crashing).
on build types"
It sounds like we have different points of view and I understand I am
contrarian on this one. But I have never heard a story about someone
having a serious problem with multiple builds this way. Surely there
was a really good story once because word seems to have gotten around.
However, for me this is just normal stuff when you work at company
that cross compiles to a number of different resource constrained
targets. I recently wrote scripts that test 20 different builds for
one code base and that seems to be a great way to write really correct
code.
If I could require assertion checks not to have side-effects it should
be safe to change the meaning of that code from build to build.
I fell out of love with printf too. Until I realized it was actually
an op-code interpreter with a validation tool available for it. But
I'll use whatever works best. That assert code I posted was designed
to work in plain C too and not just C++ which might matter in some
code bases.
The assertion macro also calls functions that can't be constexpr,
which I had thought precluded use in a manifestly const context. That
might be something I need to look at more closely, I'll admit my last
attempt at using "if constexpr" for that just refused to compile as
expected and I decided to go do something else.
Sorry I forgot about breakpoint being added. Thank you for taking the
time to respond.
From: Jan Schultke <janschultke_at_[hidden]>
Date: Mon, 25 Aug 2025 06:46:11 +0200
> I would argue there has to be support by default for making the debugger
> stop on the exact line where > the assert fired.
> There is just no excuse for having a software engineer do unnecessary manual labour
> every time an assertion fires.
Well, we have std::breakpoint() and std::breakpoint_if_debugging()
now, so that seems like what you need to make it happen.
> There are also uses for having different "levels" of severity when it comes to asserts.
"Debug" and "release" builds are outside the scope of the standard
(with the weird exception of the NDEBUG macro). I wouldn't want macros
in the standard which behave differently based on build types if there
is no such thing as "build type" in the standard in the first place.
> This didn't work reliably with constexpr
> and you have to check the generated assembly to find that out.
Everything works reliably with constexpr. If you store a value in a
constexpr variable, no matter what, it will always be
constant-evaluated. What you probably mean is that your function isn't
always being inlined and optimized away, but so is life. That can
happen with compilers.
> Next is the issue of formatting assert messages.
> It turns out to be really handy to have debug asserts
> that take C-style variadic arguments and perform printf formatting.
> There is an issue here with having untested format strings and that does need to be resolved. However, by leveraging gcc's ability to check a printf format string against its arguments
> I was able to avoid any issues in my own work.
> This approach avoids making an unnecessary local function call
> or allocating memory after an assertion fails.
You already have all the functionality you need. You can
std::format_to with some output iterator that writes to a file
character by character. This will never allocate memory, and gives you
pretty powerful formatting for your assertion messages.
C-style variadics in the C++ standard library are a terrible idea, and
I'd strongly oppose them on the basis of safety and common sense.
> The standard library also has explicit language allowing for its asserts
> to operate in a manifestly const environment.
> It would be nice for others to be able to access that functionality
> without mucking up the callstack for every assert in the codebase.
You can just denote a function as constexpr, right? What functionality
do you think you're missing? A typical assertion macro is only going
to deepen the call stack when an assertion fails, and that seems like
a bit of a non-issue, especially if you get the "correct" breakpoint
via std::breakpoint_if_debugging first. The extra call would be
avoidable if you implemented it all via macros, but that would just
increase the code size all over the place, for assertions that aren't
ever meant to trigger (or trigger once immediately before crashing).
Received on 2025-08-25 09:51:21