[HN Gopher] C++26: A User-Friednly assert() macro
___________________________________________________________________
C++26: A User-Friednly assert() macro
Author : jandeboevrie
Score : 57 points
Date : 2026-03-25 10:51 UTC (3 days ago)
(HTM) web link (www.sandordargo.com)
(TXT) w3m dump (www.sandordargo.com)
| nyc_pizzadev wrote:
| The nice thing about assert() is you can just define your own:
|
| https://github.com/fiberfs/fiberfs/blob/7e79eaabbb180b0f1a79...
|
| In this case, the ability to see the actual values that triggered
| the assert is way more helpful.
| BoingBoomTschak wrote:
| Yeah, but the macro system being so pitiful makes me long for
| one that allows something as magical as fiveam's is (https://gi
| thub.com/lispci/fiveam/blob/e43d6c8e7da5a80d5c33e8...) instead
| of having to write special cases for unary and binary
| predicates.
| omoikane wrote:
| > (assert) doesn't follow the usual SCREAMING_SNAKE_CASE
| convention we associate with macros
|
| There are a few things like that, for example:
|
| https://en.cppreference.com/w/c/numeric/math/isnan - isnan is an
| implementation defined macro.
|
| https://en.cppreference.com/w/c/io/fgetc - `getc` may be
| implemented as a macro, but often it's a function.
| nealabq wrote:
| In C++ you should probably #include <cstdio> instead of
| <stdio.h> unless you have a good reason. And especially avoid
| #including both. <cstdio> provides the function std::getc(..)
| while <stdio.h> usually provides getc(..) as a macro.
|
| htons(..) and related socket-utility names are also often
| macros, but I'm pretty sure there is not a std::htons(..) in
| the C++ standard, partly because 'htons' is not an attractive
| name. Since it's (sometimes) a macro don't qualify its
| namespace like ::htons(..).
|
| A long time ago in the Microsoft C (and later C++) dev envs
| there were macros named "min" and "max", which I thought were
| terrible names for macros.
| adzm wrote:
| > A long time ago in the Microsoft C (and later C++) dev envs
| there were macros named "min" and "max", which I thought were
| terrible names for macros.
|
| Yeah, this is still in windows.h unless you #define NOMINMAX
|
| I remember having to guard against this in some inline code
| by surrounding the c++ calls with parenthesis, eg
| `(std::min)(a, b)`
| nananana9 wrote:
| Yep. There's tons of others as as well. 16-bit x86 enjoyers
| will be happy to know there are `near` and `far` macros
| whose primary purpose in 2026 is to break my projection
| matrices. And of course every Win32 function that takes
| strings has a macro that resolves it to either the UTF-16
| or ASCII variant, so your custom CreateWindow is now a
| CreateWindowA, tough luck buddy.
|
| I usually wrap Windows.h in a header followed by 100
| #undefs to contain the disease.
| MontagFTB wrote:
| Putting code with side effects into an assert is asking for
| trouble. Compile with NDEBUG set and the effects mysteriously
| disappear! Anything beyond an equality expression or straight
| boolean should be avoided.
| nyc_pizzadev wrote:
| This is just a symptom of a bad assert() implementation, which
| funny enough is the standard. If you properly (void) it out,
| side effects are maintained.
|
| https://github.com/fiberfs/fiberfs/blob/7e79eaabbb180b0f1a79...
| omoikane wrote:
| assert() is meant to be compiled away if NDEBUG is defined,
| otherwise it shouldn't be called assert(). Given that
| assert() may be compiled away, it makes sense not to give it
| anything that has side effects.
|
| Abseil has the convention where instead of assert(), users
| call "CHECK" for checks that are guaranteed to happen at run
| time, or "DCHECK" for checks that will be compiled away when
| NDEBUG is defined.
|
| https://github.com/abseil/abseil-
| cpp/blob/0093ac6cac892086a6...
|
| https://github.com/abseil/abseil-
| cpp/blob/0093ac6cac892086a6...
| nmilo wrote:
| If your assert compiles down to `if (condition) {}` in
| production then the compiler will optimize away the
| condition while keeping any side effects.
| IshKebab wrote:
| Yeah which may not be what you want. E.g.
| `assert(expensive_to_compute() == 0)`.
|
| The correct way to solve this is with debug asserts (as
| in Rust, or how the parent described).
| jmalicki wrote:
| Side effects are bad of course, but anything beyond a straight
| boolean or equality is bad?
|
| `assert(vector.size() < 3)` is ridiculous to you?
| nealabq wrote:
| I don't mean to be that guy, but for "functional" programmers a
| print statement has "side effects".
|
| But your meaning is clear. In an assert expression, don't call
| functions that might change the program/database state. Be as
| "const" as possible.
| toxik wrote:
| Not just for functional programmers. Prints and other I/O
| operations absolutely are side effects. That's not running
| counter to the point being made. Print in an assert and
| NDEBUG takes away that behavior.
| nealabq wrote:
| You're right of course. I was thinking specifically of
| printing log/debug statements in the assert(..), but that
| usually only happens if the assert(..) fails and exits, and
| in that case the "no side effects" rule no longer matters.
| usrnm wrote:
| I once spent several days debugging that same mistake. Stuff
| worked perfectly in tests but broke misteriously in production
| builds. Couldn't stop laughing for a few minutes when I finally
| figured it out.
| maccard wrote:
| Indeed. bool is_even(int* valPtr) {
| assert(valPtr != nullptr); return *valPtr % 2;
| }
|
| Does not do what you think it does with nullptr. A major game
| engine [0] has a toggle to enable asserts in shipping builds,
| mostly for this reason
|
| [0] https://dev.epicgames.com/documentation/en-us/unreal-
| engine/...
| dccsillag wrote:
| I'm sorry, but what exactly is the problem with the code?
| I've been staring at it for quite a while now and still don't
| see what is counterintuitive about it.
| IshKebab wrote:
| There's nothing wrong with it. It does exactly what you
| think it does when passed null.
| dataflow wrote:
| Depends on where you're coming from, but some people would
| expect it to enforce that the pointer is non-null, then
| proceed. Which would actually give you a guaranteed crash
| in case it is null. But that's not what it does in C++, and
| I could see it not being entirely obvious.
| IshKebab wrote:
| Assert doesn't work like that in any language.
| mhh__ wrote:
| This is a very "Dr Dr it hurts when I do this" "Don't do
| that" one it must be said.
| secondcoming wrote:
| Let's not vague post on HN. What's the problem with the
| above?
| samiv wrote:
| That's why you define your own assert macro and keep in on
| unconditionally. Your programs will be better for it.
| jandrewrogers wrote:
| An assertion can be arbitrarily expensive to evaluate. This
| may be worth the cost in a debug build but not in a release
| build. If all of assertions are cheap, they likely are not
| checking nearly as much as they could or should.
| samiv wrote:
| Possibly but I've never seen it in practice that some
| assert evaluation would be the first thing to optimize.
| Anyway should that happen then consider removing just that
| assert.
|
| That being said being slow or fast is kinda moot point if
| the program is not correct. So my advisor to leave always
| all asserts in. Offensive programming.
| andrepd wrote:
| Rust has assert and debug_assert, which are self-explanatory.
| But it also has an assert_unchecked, which is what other
| languages incl C++ call an "assume" (meaning "this condition
| not holding is undefined behaviour"), with the added bonus that
| debug builds assert that the condition is true.
| bluGill wrote:
| Related our logging system has a debug which is not logged by
| default but can be turned on if a problem in an area is found
| (in addition to the normal error/info which is logged). I had
| the idea that if a test fails we should print all these debugs
| - easy enough to turn on but a number of tests failed because
| of side effects that didn't show up when off.
|
| i'm trying to think of how/if we can run tests with all logging
| off to find the error and info logs with side effects.
| throwpoaster wrote:
| assert(spellcheck("Friednly"));
| nananana9 wrote:
| spellcheck.cpp:1:19: error: unexpected character <U+201C>
| 1 | assert(spellcheck("Friednly")); |
| ^
| amelius wrote:
| Shouldn't the preprocessor be fixed, if it trips that easily on
| common C++ constructs?
| tom_ wrote:
| I'm sure the standardization committee are always looking for
| fresh ideas!
| marginalia_nu wrote:
| Preprocessor is just doing text transformations on the sources.
|
| It's not really something that can be fixed, other than moving
| away from the preprocessor and putting metaprogramming
| capabilities into the language itself (which C++ has been
| doing).
| amelius wrote:
| I mean, you could extend it such that a simple comma has no
| special meaning.
|
| But I agree, fewer special tricks is better and that includes
| the preprocessor.
| semiinfinitely wrote:
| "C++47: Finally, a Standard Way to Split a String by Delimiter"
| porise wrote:
| I'm still waiting for C++ to support Unicode properly.
| einpoklum wrote:
| A standard way to split a string? Well, what's wrong with:
| std::views::split(my_string, delimeter) ?
| nananana9 wrote:
| Template bloat, terrible compile errors, terrible debug build
| performance, 1 second of extra compile time per cpp file when
| you include ranges, and you can't step through it in a
| debugger.
| grokcodec wrote:
| Friedns shouldn't let Freidns post on HN without running spell
| check
| adzm wrote:
| One of my favorite things from ATL/WTL was the _ASSERT_E macro
| which additionally converts the source expression to text for a
| better message to be logged
| WalterBright wrote:
| D just makes assert() part of the language:
|
| https://dlang.org/spec/expression.html#assert_expressions
|
| The behavior of it can be set with a compiler switch to one of:
|
| 1. Immediately halting via execution of a special CPU instruction
|
| 2. Aborting the program
|
| 3. Calling the assert failure function in the corresponding C
| runtime library
|
| 4. Throwing the AssertError exception in the D runtime library
|
| So there's no issue with parsing it. The compiler also
| understands the semantics of assert(), and so things like
| `assert(0)` can be recognized as being the end of the program.
| wpollock wrote:
| > assert(x > 0 && "x was not greater than zero");
|
| Shouldn't that be "||" rather than "&&"? We want the message only
| if the boolean expression is false.
| puschkinfr wrote:
| No, because the string will be implicitly converted to `true`
| and `(a && true) == a` (for boolean `a`), so it will only be
| `false` if the assertion fails. Using || would always evaluate
| to `true`
| wpollock wrote:
| Thanks. However, I'm still not certain this will work as
| intended. In C++ "&&" is short-circuiting; if the first part
| of the expression is false then the second part is never
| evaluated. So the result is never the message.
|
| From <https://en.cppreference.com/w/cpp/language/operator_log
| ical....>: For the built-in logical AND operator, the result
| is true if both operands are true. Otherwise, the result is
| false. This operator is short-circuiting: if the first
| operand is false, the second operand is not evaluated.
___________________________________________________________________
(page generated 2026-03-28 23:00 UTC)