\

C++26: A User-Friednly assert() macro

48 points - last Wednesday at 10:51 AM

Source
  • MontagFTB

    today at 4:23 PM

    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.

      • usrnm

        today at 5:49 PM

        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

          today at 6:45 PM

          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/...

            • mhh__

              today at 7:42 PM

              This is a very "Dr Dr it hurts when I do this" "Don't do that" one it must be said.

              • dccsillag

                today at 6:59 PM

                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

                    today at 7:12 PM

                    There's nothing wrong with it. It does exactly what you think it does when passed null.

            • samiv

              today at 7:02 PM

              That's why you define your own assert macro and keep in on unconditionally. Your programs will be better for it.

                • jandrewrogers

                  today at 7:26 PM

                  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

                      today at 7:34 PM

                      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.

              • nyc_pizzadev

                today at 4:49 PM

                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...

              • jmalicki

                today at 4:59 PM

                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

                  today at 5:46 PM

                  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

                      today at 6:32 PM

                      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.

              • omoikane

                today at 4:11 PM

                > (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

                    today at 5:41 PM

                    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

                        today at 7:34 PM

                        > 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

                            today at 7:41 PM

                            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.

                        • today at 7:39 PM

                  • nyc_pizzadev

                    today at 3:49 PM

                    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.

                  • adzm

                    today at 7:36 PM

                    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

                    • grokcodec

                      today at 5:32 PM

                      Friedns shouldn't let Freidns post on HN without running spell check

                      • last Wednesday at 11:30 AM

                        • amelius

                          today at 5:13 PM

                          Shouldn't the preprocessor be fixed, if it trips that easily on common C++ constructs?

                            • marginalia_nu

                              today at 5:33 PM

                              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

                                  today at 7:00 PM

                                  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.

                                    • today at 7:48 PM

                              • tom_

                                today at 5:16 PM

                                I'm sure the standardization committee are always looking for fresh ideas!

                            • semiinfinitely

                              today at 5:16 PM

                              "C++47: Finally, a Standard Way to Split a String by Delimiter"

                                • porise

                                  today at 6:24 PM

                                  I'm still waiting for C++ to support Unicode properly.

                                  • einpoklum

                                    today at 7:45 PM

                                    A standard way to split a string? Well, what's wrong with:

                                        std::views::split(my_string, delimeter)
                                    
                                    ?

                                      • nananana9

                                        today at 7:51 PM

                                        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.

                                • throwpoaster

                                  today at 5:12 PM

                                  assert(spellcheck(โ€œFriednlyโ€));

                                    • nananana9

                                      today at 7:54 PM

                                        spellcheck.cpp:1:19: error: unexpected character <U+201C>
                                            1 | assert(spellcheck(โ€œFriednlyโ€));
                                              |                   ^