[HN Gopher] C++17's useful features for embedded systems
       ___________________________________________________________________
        
       C++17's useful features for embedded systems
        
       Author : manchoz
       Score  : 179 points
       Date   : 2023-05-29 14:09 UTC (8 hours ago)
        
 (HTM) web link (interrupt.memfault.com)
 (TXT) w3m dump (interrupt.memfault.com)
        
       | cmrdporcupine wrote:
       | In-line class static variables ... finally.
       | 
       | if constexpr is neat (along with a bunch of the other
       | constexpr/compile-time features that have been coming along), but
       | I feel like this will have both... good and bad uses, and I fear
       | for the astronauts who will go crazy with this.
       | 
       | The enhanced conditionals, this I kind of like though it would
       | take some while to get used to... kind of surprised this got in,
       | being such a departure from C.
       | 
       | Small thing: hardware_destructive_interference_size is nice. Wish
       | I had this in Rust.
       | 
       | Looks like it was asked for (https://github.com/rust-
       | lang/rfcs/issues/1756) but went nowhere.
        
         | gumby wrote:
         | > kind of surprised this got in, being such a departure from C.
         | 
         | While completely gratuitous incompatibilities with C are not
         | welcome, C compatibility in general was abandoned in new
         | features long ago (consider range based `for` or the venerable
         | `nullptr`).
        
           | unwind wrote:
           | Luckily (?) nullptr is in C23 so yay!
        
             | gumby wrote:
             | So kind of the C committee to help C++ reduce its
             | incompatibility with standard C :-)
        
         | AlotOfReading wrote:
         | I have a strong dislike for the enhanced conditionals. Like,
         | the feature itself is fine... but there's just a certain subset
         | of people who think any new C++ feature is the blessed "proper"
         | way to accomplish any task and this feature is _very_ abusable.
         | It also doesn 't provide very much benefit to actual practice
         | in return.
        
           | secondcoming wrote:
           | Having the compiler deduce the return type of a function
           | depending on the constexpr path taken is really useful.
        
             | AlotOfReading wrote:
             | Different feature, I'm talking about the optional
             | initialization section added to conditionals. It allows
             | moving declarations to the point of use like for
             | statements. I've also met people who think it's now the
             | appropriate place to declare all block scope variables,
             | which is horrifying.
        
               | cmrdporcupine wrote:
               | It's horrifying but maybe also intriguing?
               | 
               | Has shades of the OCaml/StandardML "let ... " scope:
               | 
               | StandardML:                 let         val a = 1
               | val b = 2       in         a + b       end
               | 
               | It's nice for making it clear that the scope of the
               | assignment is restricted to this lexical scope, though
               | there are other ways to do this in C/C++.
               | 
               | I work in Rust these days so haven't had a chance to use
               | much of this, but I wonder if this has some nice RAII
               | type use patterns? I may now go explore.
        
           | einpoklum wrote:
           | > I have a strong dislike for the enhanced conditionals.
           | 
           | So don't use them. C++ is a multi-paradigmatic language, you
           | don't have to (and typically can't, really) put all features
           | to use.
           | 
           | I'm not such a great fan of them either, but it's not like
           | they would confuse me if I saw them in somebody else's code.
           | 
           | https://www.youtube.com/watch?v=cWSh4ZxAr7E&list=TLPQMjkwNTI.
           | ..
        
       | mhh__ wrote:
       | `if constexpr` is such a disaster. They were so close to getting
       | it right (not introducing a scope) but they missed.
       | 
       | Similarly constexpr itself is also genuinely ridiculous: (I have
       | said this on hackernews before) It's such a stupid idea to
       | require an annotation everywhere you want to evaluate things at
       | compile time, practically everything will inevitably be
       | evaluatable at compile time, and you need the implementation
       | anyway, so just let it fail rather than ask for permission
       | everywhere.
       | 
       | Having the keyword for variables and constants is fine (i.e. top
       | down and bottom up constraints need to be dictated) but you
       | shouldn't need to write constexpr more than that.
        
         | titzer wrote:
         | They should just skip to the end and make the entire language
         | legal to execute at compile-time. Virgil does this and many
         | Lisps before it. You then get a heap against which the entire
         | program can be optimized at compile time. C++ is slowly
         | catching up to Virgil I for microcontrollers, ala the 2006
         | paper.
        
         | FpUser wrote:
         | I would absolutely hate `constexpr` to be implicit and left to
         | be decided by compiler.
        
           | WalterBright wrote:
           | How D decides it is if the grammar says its a constant-
           | expression, whatever makes up the constant-expression gets
           | evaluated at compile time - functions and all. For example,
           | int square(int x) { return x * x; }              static
           | assert(square(3) == 9);
           | 
           | This allows one to write unit tests that are checked at
           | compile time, whereas:                   int main() {
           | assert(square(3) == 9); }
           | 
           | is always a runtime check.
           | 
           | Compile time unit testing is a significant win:
           | 
           | 1. it's always more productive to find problems at compile
           | time rather than run time
           | 
           | 2. it isn't necessary to conditionally turn off compilation
           | of the tests for the release build
           | 
           | 3. you can't forget to run the tests before shipping
        
           | thebigwinning wrote:
           | What if it was always allowed to do compile time, but you
           | could add a keyword to assert it did (like force-inline).
           | 
           | That way you get benefits by default. Whereas right now it's
           | not allowed to.
        
             | masklinn wrote:
             | These are two completely different things.
             | 
             | One is the provider of the API promising that the function
             | supports compile-time evaluation.
             | 
             | The second is the consumer of the API ensuring that an
             | expression has been compile-time evaluated. It's nice that
             | the consumer can make sure, but that's not helpful if the
             | provider of the API never specifically intended for a
             | function to be evaluatable at compile-time.
             | 
             | > Whereas right now it's not allowed to.
             | 
             | Of course it's _allowed_ , but you're at the mercy of the
             | compiler's decisions.
        
               | thebigwinning wrote:
               | > Of course it's allowed,
               | 
               | It's not. If you to use a function in a constexpr context
               | then it will complain it needs to be marked as such. So
               | the API creator needs to label every function in case a
               | client wants to use it.
        
           | mhh__ wrote:
           | It is decided by the compiler.
           | 
           | Note that I mean at the declaration not the callsite.
        
         | gumby wrote:
         | I wish const _expr_ actually asked the compiler to evaluate an
         | _expression_ at compile time and not just a statement.
        
           | WalterBright wrote:
           | In D, every part of the grammar that is a constant-expression
           | must be compiled at compile time. For example:
           | void test() {              int square(int y) { return y * y;
           | }         int x = square(3);  // evaluate at square at run
           | time         enum y = square(3); // evaluate at square
           | compile time              }
           | 
           | (Although, when the optimizer gets through inlining square(),
           | etc., it will wind up at compile time anyway.)
        
             | gumby wrote:
             | I think a compliant c++ implementation is allowed to do
             | this and I think all do so at least for straightforward
             | cases.
        
               | mhh__ wrote:
               | It's allowed to but you aren't allowed to assume.
        
         | Espressosaurus wrote:
         | constexpr is great. I can finally do most of the compile time
         | computation that previously required template metaprogramming,
         | and it's much more readable by comparison. C++17 makes it much
         | more ergonomic to use too over C++11. I can't wait for us to
         | finally upgrade our toolchain to take advantage of C++20.
         | 
         | If you're in embedded and you're not pushing everything you can
         | into constexpr, you're missing out on correctness and code size
         | benefits.
        
           | mhh__ wrote:
           | Doing stuff at compile time is good (although the compiler
           | can do 99% of it anyway its nice to have guarantees), C++
           | just got it wrong.
        
             | Espressosaurus wrote:
             | It could certainly be better, but in a world where my
             | options are C, C++, or assembly, I pick C++ every day.
             | 
             | I do keep hoping for something else to get big enough we
             | can use it because C++ sucks, but is still better than the
             | alternatives we can use.
        
               | titzer wrote:
               | Virgil doesn't have all the backends for the diversity of
               | embedded ISAs out there, but the first version compiled
               | to C and ran in as little as dozens of bytes of memory
               | (on 8-bit AVR). Nowadays I am not doing embedded systems,
               | otherwise I'd write more backends.
        
               | mhh__ wrote:
               | C++ is _much_ better than C. Personally I use D
               | everywhere I would 've previously used C++.
        
         | moron4hire wrote:
         | The example in the article is a little weird, too. Putting
         | aside the weirdness of the intended purpose of the example,
         | what's wrong with function overloading? I think the overloaded
         | version is a lot easier to read. With the `if constexpr`
         | version, now I have to edit the one function with new cases if
         | I add a type that doesn't fit the "number or string" pattern,
         | whereas with overloading I just implement a new `length`
         | function.
        
           | mhh__ wrote:
           | The example is weak but a good thing to look into is
           | https://dconf.org/2017/talks/alexandrescu.pdf i.e. this type
           | of compile time branching (when implemented properly as
           | mentioned above) lets you write code that "reacts" to other
           | code in the project's capabilities. Allows some very nice
           | patterns within a C++ flavoured type system.
        
         | masklinn wrote:
         | > It's such a stupid idea to require an annotation everywhere
         | you want to evaluate things at compile time, practically
         | everything will inevitably be evaluatable at compile time
         | 
         | And making constexpr-ness an explicit contract makes sense to
         | me: if it's not that it can be an unexpected property, and can
         | break at any change of implementation.
         | 
         | Yes requiring a function being marked requires that the
         | implementor do it, but it also means they have considered this
         | use-case and made it officially part of the API. It's not a
         | trivial promise to make.
         | 
         | > and you need the implementation anyway, so just let it fail
         | rather than ask for permission everywhere.
         | 
         | "Let it fail" is the issue, if it's implicit a user can assume
         | this is working by design, then find their program stops
         | compiling on the next release not because the maintainer
         | wilfully broke the API, but because they changed an
         | implementation detail of the function and constexpr-ness is not
         | something they considered (or assumed correct) in the first
         | place.
         | 
         | Maybe the language should work the other way around and
         | everything should be constexpr by default and functions should
         | opt _out_ of constexpr-ness, but that 's 40 years too late for
         | C++. And I can't think of any langage which does that. And
         | frankly it feels like the wrong default for the reasons above.
        
           | Jasper_ wrote:
           | Except constexpr isn't a guarantee. Compilers can choose to
           | silently evaluate constexpr things at runtime. And I _have_
           | ran into this before: a compiler with a recursion limit
           | causing it to bail on constexpr and just emit the code.
           | 
           | So constexpr isn't a guarantee of it being evaluated at
           | compile time, and non-constexpr isn't a guarantee of it being
           | evaluated at runtime. Cool, huh?
        
             | masklinn wrote:
             | Mostly makes sense to me: constexpr means the function is
             | evaluat _able_ at compile-time, being callable at runtime
             | is part of the contract and why _adding_ constexpr does not
             | change the API.
             | 
             | For an assertion that something is evaluated at compile
             | time I'd assume a variable-level annotation (in a non-
             | constexpr function). Though I don't know c++ enough to have
             | any idea whether that's the case.
             | 
             | And contant evaluation optimisation has always been a thing
             | so it's not really surprising.
        
               | Dylan16807 wrote:
               | > Mostly makes sense to me: constexpr means the function
               | is evaluatable at compile-time, being callable at runtime
               | is part of the contract and why adding constexpr does not
               | change the API.
               | 
               | The problem isn't that you're _able_ to call it at
               | runtime with runtime data, it 's that if you give it
               | compile-time data you have no idea when it will be run.
               | 
               | > For an assertion that something is evaluated at compile
               | time I'd assume a variable-level annotation (in a non-
               | constexpr function). Though I don't know c++ enough to
               | have any idea whether that's the case.
               | 
               | Oh, were you under the impression that constexpr was just
               | for functions? It applies to variables too, and it's not
               | a guarantee on them. You need to use other, newer
               | annotations.
        
             | jcelerier wrote:
             | > constexpr isn't a guarantee of it being evaluated at
             | compile time
             | 
             | constexpr is a guarantee that you can use the thing in a
             | constexpr context, and this is where the "evaluated at
             | compile-time" guarantee can come from:
             | template<typename T>         auto func() {           //
             | here some compilers can still choose to evaluate x at run-
             | time - and very likely all of them if no optimizations are
             | enabled           constexpr int x = f();                 //
             | but here it becomes mandatory for this use of x to be
             | evaluated at compile-time, since the number is literally
             | going to be part of the compiled binary as part of the
             | function name mangling           return
             | std::integral_constant<int, x>{};         }
        
               | gpderetta wrote:
               | Constexpr does not guarantee that a constexpr function
               | can be called at compile time, unfortunately. Only that
               | it can be called at runtime for a subset of all possible
               | parameters.
               | 
               | Except of course the subset of parameters for which it is
               | constexpr callable can't be checked at function
               | definition time (if it exists at all), only at function
               | invocation time.
               | 
               | Which makes the constexpr annotation useless and it is in
               | only because the authors couldn't otherwise get the paper
               | through some committee objections.
        
               | Dylan16807 wrote:
               | Is such a mangling mandated by the standard?
        
               | mhh__ wrote:
               | Mangling is mentioned in the standard.
               | 
               | In this case though the underlying reason is that its
               | part of the type (system) not because of the mangle
               | specifically.
        
               | Dylan16807 wrote:
               | > Mangling is mentioned in the standard.
               | 
               | Forgive me, but can you be clearer than "mentioned"? Is
               | the mangling required to contain template parameters for
               | return types?
               | 
               | > In this case though the underlying reason is that its
               | part of the type (system) not because of the mangle
               | specifically.
               | 
               | I'm not sure. The compiler knows it will always be the
               | same type, so under many uses of this function I could
               | easily imagine a compiler that doesn't actually fill in
               | .value until runtime.
        
               | mhh__ wrote:
               | Mangling isn't technically required as per se but it's by
               | far the most common approach (basically a local minimum
               | in terms of cost)
               | 
               | I think this subset of C++ templates is probably
               | undecidable still.
        
             | comex wrote:
             | C++20 has consteval and constinit to fix this (at the cost
             | of making things even more complicated).
        
               | MrBuddyCasino wrote:
               | Amazing.
        
           | throwaway173738 wrote:
           | The other problem with implicit constexpr is that it can
           | change the timing characteristics of your code depending on
           | whether your input data is known at compile time. And then if
           | you want it to be constexpr you have to troubleshoot why it
           | sometimes isn't. I think this approach of maximal control in
           | exchange for some foot guns is very much in keeping with C++.
        
             | WalterBright wrote:
             | In D you don't have to troubleshoot it. The compiler will
             | tell you where the problem is if it can't be evaluated at
             | compile time.
             | 
             | > for some foot guns
             | 
             | There just aren't any with this feature.
        
           | scatters wrote:
           | > Maybe the language should work the other way around and
           | everything should be constexpr by default and functions
           | should opt out of constexpr-ness, but that's 40 years too
           | late for C++.
           | 
           | That's how lambdas behave in C++, so it's definitely
           | feasible. gcc has a flag, -fimplicit-constexpr I think, that
           | does this for regular functions, and it doesn't appear to
           | cause any significant issues. I think there has been some
           | talk around making this the language behavior at some point
           | in the future.
        
           | mhh__ wrote:
           | > It's not a trivial promise to make.
           | 
           | It's actually very trivial. 99% of code I write can be run at
           | compile time.
           | 
           | > maintainer wilfully broke the API
           | 
           | Did they break the API or did they change the semantics? The
           | API is tittle-tattle, the point is that you _cannot_ run it
           | at compile time anymore (e.g. accidentally introduced a
           | opaque new dependency by accident? Oops)
           | 
           | > And I can't think of any langage which does that
           | 
           | Being able to opt out is not what you want, but D allows
           | everything to at least attempt to be run at compile time.
           | This has been very profitable.
        
             | masklinn wrote:
             | > 99% of code I write can be run at compile time.
             | 
             | Good for you, you missed the point.
             | 
             | > Did they break the API or did they change the semantics?
             | 
             | What are you even on about?
             | 
             | > The API is tittle-tattle
             | 
             | What are you even on about, part 2.
             | 
             | > the point is that you cannot run it at compile time
             | anymore
             | 
             | Yes, the API allowed one thing, now it does not. That's no
             | different from changing an optional parameter to mandatory
             | or any other thing which breaks the API.
        
               | mhh__ wrote:
               | > Good for you, you missed the point.
               | 
               | >> Did they break the API or did they change the
               | semantics?
               | 
               | > What are you even on about?
               | 
               | Constructive.
        
           | WalterBright wrote:
           | > I can't think of any langage which does that
           | 
           | D does. And it's a very popular feature. In fact, D goes even
           | further - only the _path_ taken through a function needs to
           | conform when running it at compile time, not 100% of the
           | function.
           | 
           | > it feels like the wrong default for the reasons above.
           | 
           | In about 16 years of very extensive use, it has never been
           | brought up as a problem.
        
         | jenadine wrote:
         | Disaster? Just because it introduces a scope? Please elaborate.
         | A scope is the right thing to do, every other {} introduce a
         | scope, so if this wouldn't have been consistent with the normal
         | 'if' that would have been extra confusing.
         | 
         | Maybe you'd like to do something like:
         | template<typename T>         struct ABC {            int x;
         | int y;            if constexpr (is_3d<T>) {               int
         | z;             }          };
         | 
         | But yeah, that's not what if constexpr does.
        
           | dymk wrote:
           | That's exactly what it could have allowed, and become a much
           | more powerful construct in the language
        
           | mhh__ wrote:
           | Which is why it's crap => back to macros we go.
        
         | jcelerier wrote:
         | > `if constexpr` is such a disaster.
         | 
         | to me it's been a very useful tool for reflection, for instance
         | if constexpr (requires { foo.someMember; }) {
         | use(foo.hasSomeMember);         } else {
         | some_fallback_case();         }
        
           | mhh__ wrote:
           | This is "design by introspection". It works a lot better if
           | you can
           | 
           | 1. Do this in types. 2. Do this without introducing a new
           | scope inside a function.
        
       | [deleted]
        
       | mhh__ wrote:
       | Wrt hardware_destructive_interference_size, what happens if you
       | compile for X86 which is then run on a machine (Rosetta) that has
       | a (2x) bigger cacheline internally?
        
         | [deleted]
        
         | addaon wrote:
         | x86 (generalized: an ISA) doesn't have an inherent cacheline
         | size, a given implementation of that ISA does.
         | std::hardware_destructive_interference_size and friends are
         | compile time constants, and are defined based not just on the
         | target ISA, but the target implementation (see -march and -cpu
         | for gcc and clang).
        
           | cmrdporcupine wrote:
           | It does sort of bring up the general question of why this is
           | a compile time and not runtime constant. I doubt x86 will
           | double its cache-line size any time soon, but if it did --
           | and people are running binaries with cache-padding at
           | 64-bytes -- expected behaviour is going to differ. Not in a
           | way that's going to make anybody lose their minds, mind you,
           | but this kind of micro-optimization will just cease to be
           | effective.
           | 
           | EDIT: naturally I understand that compile makes sense for
           | e.g. statically sizing array sizes etc.
        
             | aw1621107 wrote:
             | Raymond Chen discussed that in a relatively recent blog
             | post [0]. The main points are that those constants are
             | typically used to influence struct layouts/alignments, and
             | those must be decided at compile time. The alternative is
             | to generate multiple versions of a struct/other code with
             | different alignments/layouts and choose among them at
             | runtime, but that comes with other tradeoffs.
             | 
             | [0]: https://devblogs.microsoft.com/oldnewthing/20230424-00
             | /?p=10...
        
             | jeffbee wrote:
             | > I doubt x86 will double its cache-line size any time soon
             | 
             | Well, why not? They doubled it from 32B to 64B between the
             | Pentium III and Pentium IV.
        
               | cmrdporcupine wrote:
               | I don't know enough about CPU design to say really but
               | Pentium IV is a _long_ time ago now. And since then, I
               | suspect that a lot of assumptions about 64B line sizes
               | have been baked in.
               | 
               | Apple could go to 128 because they were rolling out a
               | whole new ISA, so were breaking compat anyways.
               | 
               | That said, they have amazing performance on the M1, and I
               | wonder how much of that has to do with the wider L1 size.
        
           | jeffbee wrote:
           | True, but the answer of the asked question is if you target a
           | 64B cache line and run with a 128B line you may get more
           | sharing with the consequent performance and scalability
           | problems. Of course, if you are all that sensitive to such
           | matters why are you running on an emulated machine? And Apple
           | Silicon doesn't offer many hardware threads anyway, so
           | contentions problems are never very severe.
        
           | mhh__ wrote:
           | I'm not aware of any widespread X86 chips with a line size
           | bigger than 64 bytes. The ISA also practically does have a
           | minimum line size implied by the memory model, hands waving.
           | 
           | My point is that it varies at runtime but the type in the
           | standard is constexpr so you can't actually rely on it unless
           | you actually control where it executes.
        
         | wyldfire wrote:
         | You get potentially poorer cache performance because your
         | alignment value is wrong. Depending on how you used the value,
         | I suppose.
        
       | nuancebydefault wrote:
       | The whole discussion about constexpr (even though a useful
       | feature), is one example, out of many, of what a f up language
       | C++ is/has become. It's astonishing how many people have to
       | say... I don't like the language but there's no good alternative
       | for the context we are working in.
        
         | einpoklum wrote:
         | > of what a f up language C++ is/has become
         | 
         | I believe this misconception on your part stems from a lack of
         | awareness (or understanding) of C++ design goals as a language.
         | 
         | Have a look at this writeup: https://www.open-
         | std.org/jtc1/sc22/wg21/docs/papers/2020/p21... (caveat: not
         | formally adopted)
         | 
         | constexpr supports the following goals:
         | 
         | * Provide the programmer control over every aspect of
         | performance: Compile-time vs run-time execution is an aspect of
         | performance.
         | 
         | * Excellent ergonomic: Using constexpr/consteval improves on
         | earlier mechanisms for achieving compile-time evaluation, such
         | as macros, template meta-programming etc. Those are unwieldy,
         | often difficult to read, and inflexible (without jumping
         | through hoops). constexpr is not.
         | 
         | etc. etc.
         | 
         | and marginally supports the following goals:
         | 
         | * Code should perform predictably: Control over what exactly
         | runs at run-time improves predictability.
         | 
         | * Leave no room for a lower level language: While constexpr is
         | neither low nor high as a language feature, one can argue that
         | having it undercuts the potential for, say, a "C +
         | constexpr"-like language, or another lower-level language with
         | a more elaborate macro system.
         | 
         | etc. etc.
         | 
         | I could go on naming goals constexpr supports, there are lots
         | more. I challenge you to find goals from which it detracts.
        
         | elcritch wrote:
         | Nim works well in embedded contexts. It can compile to C89 or
         | C++ and integrates easily with most any C compiler. I've used
         | it with FreeRTOS, Zephyr, and bare metal. It's a smallish
         | community but there's a few shipping products using it and the
         | language is really easy to learn if you know C/C++/Python.
         | 
         | Executables from Nim also tend to stay pretty lightweight even
         | when using generics / templates.
        
         | yagni_dev wrote:
         | the only astonishing thing is how pathetic rustafarians are.
         | Whenever someone writes something positive about a c++ feature
         | the squad is working extra time to diminish the
         | article/opinion. You are trying so hard, lol.... "constexptr
         | if" is brilliant, simplifies a lot template code.
        
         | FpUser wrote:
         | The language is not fucked up. Come back to Rust in 40 years
         | and see what has become of it if it still not Cobol type
         | legacy. Also I do not think you can compare it to C++, it is
         | too limited for that in my opinion. I would rather consider it
         | as "fixed" C that satisfies some safety constraints.
         | 
         | What is fucked up is us not being perfect machines and not
         | producing ideal design from the first try and then having to
         | deal with compatibility. Well duh.
        
         | staunton wrote:
         | Especially with embedded applications, the issue is lack of
         | tooling and sluggish vendors. For them, even C++ is "too new"
         | in some cases to properly support.
         | 
         | Rust is making good progress despite these issues but it's
         | still a pain to use in anything but the most common systems. As
         | soon as you're using SoCs that have an FPGA part, for example,
         | you're forced to use proprietary vendor tools and good luck
         | getting Rust to work with those in the next few decades...
        
           | cmrdporcupine wrote:
           | TBH Rust in the deep embedded space -- like MCUs, not MPUs --
           | when I looked at it a bit ago, it was fairly dubious how much
           | intense effort would be required to a) trim fat to get
           | reasonable binary sizes b) keep the fat under control. I'm
           | used to doing much of this with C++, but it seemed way easier
           | to end up with a bloated binary in Rust <I'm looking at you,
           | panic handler!>
           | 
           | Starting a job in a week which is embedded Rust, but more on
           | the Linux-on-SoC side, we'll see it goes.
        
             | steveklabnik wrote:
             | I'd suspect I has to do more with familiarity than it does
             | intrinsic differences between the two. My team does this
             | kind of work, and it's not difficult. It is worth tracking,
             | paying attention to, and making changes if you've done
             | something wrong, but what things cause these issues is
             | generally pretty straightforward.
        
         | jnwatson wrote:
         | Rust. The alternative is Rust.
        
           | nuancebydefault wrote:
           | I hope you will be proven right!
        
           | marsven_422 wrote:
           | [dead]
        
       | shadowgovt wrote:
       | So much of modern c++ is trying to get around just using the
       | preprocessor.
        
         | gumby wrote:
         | The preprocessor is such a botch (charitably, state of the art
         | 1975). As Stroustrup said to me once in a discussion on the
         | topic, "Once an ecological niche has been colonized it's almost
         | impossible to clean up, so you have to work around it."
        
           | shadowgovt wrote:
           | Yeah, but they're trying to replace it with more botch. "If
           | constexpr can be used to precompute statically-determinable
           | logic, except except except..."
        
             | ok123456 wrote:
             | Constexpr can replace most per-processor macros in a subset
             | of C++. This is a huge improvement over preprocessor macros
             | and functions which are very difficult to refactor. Even if
             | it turns out to be 'botch', we can reason about the 'botch'
             | and find ways to mitigate problems with it through static
             | analysis.
        
         | Warwolt wrote:
         | And for good reason?
        
           | randyrand wrote:
           | maybe. the preprocessor is much simpler to understand than
           | constexpr and friends
        
             | einpoklum wrote:
             | Not if you're a compiler or a static analysis tool...
        
         | WalterBright wrote:
         | C++ should finish the job and deprecate the preprocessor.
        
           | gpderetta wrote:
           | Modules get rid of includes and includes guards.
           | 
           | Unfortunately the preprocessor is still needed for
           | stringyfication and other symbol manipulation.
           | 
           | C++26 (2126 of course) will introduce enough reflection to
           | finally get rid of the preprocessor.
        
             | WalterBright wrote:
             | I added enough to D that it didn't need a preprocessor, yet
             | could do all those things.
        
               | shadowgovt wrote:
               | I really need to get around to looking into D.
        
           | shadowgovt wrote:
           | To be honest, if that happened I'd be 100% on board. My issue
           | is combinatoric complexity, not a new way to do things. I'm
           | not frustrated that we have constexpr; I'm frustrated I now
           | have to worry about how it interacts with macros and
           | namespaces and lifetimes and oh what if the values are
           | templates and so on.
           | 
           | Each feature adds superlinear cost to understanding the
           | language and the code you're reading.
        
         | paulddraper wrote:
         | > So much of modern c++ is trying to get around just using the
         | preprocessor.
         | 
         | ...and that's a good thing
        
       | swader999 wrote:
       | I'm sure other languages would have more than 17 useful features.
        
         | FpUser wrote:
         | Is it "more than 17 features" or more "features than C++17"?
         | For a programmer your way of formulating questions is a strange
         | one.
        
           | gumby wrote:
           | Whoosh!
        
       | kramerger wrote:
       | Why                   uint8_t b = 0b1111'1111;
       | 
       | I would rather have                   uint8_t b = 0b1111_1111;
       | 
       | This ' thing is hard to get right on some non-us keyboards. And
       | yes, I've the same problem with Rust.
        
         | dxuh wrote:
         | I think you can't do that, because the underscore may start a
         | user defined literal suffix.
        
         | cmovq wrote:
         | It could be misinterpreted as a user literal [1], for example
         | with 0xAAAA_BBBB it is unclear whether _BBBB is a user literal.
         | The original proposal discusses some of the alternatives [2].
         | 
         | [1] https://en.cppreference.com/w/cpp/language/user_literal
         | 
         | [2] https://www.open-
         | std.org/jtc1/sc22/wg21/docs/papers/2013/n34...
        
       | smarx007 wrote:
       | Isn't polymorphic memory allocator the most significant recent
       | C++ addition for embedded systems that allows preventing runtime
       | memory allocation after the init phase and thus allows conformity
       | to MISRA and other guidelines for critical SW development (esp.
       | when using stdlib)?
        
         | einpoklum wrote:
         | > Isn't polymorphic memory allocator the most significant
         | recent C++ addition for embedded systems
         | 
         | I would say no. Run-time polymorphism is overrated IMHO, and
         | more so for embedded systems, again IMHO. C++ in general has
         | been moving towards preferring things happening statically
         | rather than dynamically.
         | 
         | > that allows preventing runtime memory allocation after the
         | init phase
         | 
         | That's not what allows preventing runtime memory allocation
         | after an "init phase". Unless I'm misunderstanding what you
         | mean.
         | 
         | ... oh, I think I get it: As a general rule, avoid using
         | standard library data structures with allocators. They may work
         | fine as boilerplate, but are usually not what you want when you
         | have any non-trivial requirements. std::vectors are fine if you
         | don't mind the allocations - but you do, so not even those. You
         | could use custom allocators, but that whole mechanism is
         | totally broken design in the opinion of many; see Andrei
         | Alexandrescu's talk about this subject:
         | https://www.youtube.com/watch?v=LIb3L4vKZ7U
        
         | jeffbee wrote:
         | You could always run your stl with your favorite allocator. PMR
         | just makes it simpler to use, and provides library
         | implementations of common allocator patterns. PMR is not a
         | zero-cost abstraction, though. Its implementation via type
         | erasure has performance costs.
        
       | tonetheman wrote:
       | What compiler is he/she using? I cannot get this to compile at
       | all but I might not know the magic compiler incantation to get it
       | to work.                 template<typename T>       auto
       | length(const T& value) noexcept {           if constexpr
       | (std::integral<T>::value) { // is number               return
       | value;           }           else {               return
       | value.length();           }       }
        
         | Leherenn wrote:
         | How do you call it and what is your error message?
        
         | secondcoming wrote:
         | Like this maybe?
         | 
         | https://godbolt.org/z/6fnhT6h9v
        
           | tonetheman wrote:
           | Yeah I was using godbolt too and wondered why I could not get
           | it to compile. Just funny the article is about c++17 but you
           | need c++20 on there to get it to compile.
           | 
           | My c++ is super old. But thanks that is great I could not
           | figure it out.
        
         | Thorrez wrote:
         | std::integral was added in C++20 , so this example will only
         | work in C++20. Weird that the author used this example.
         | 
         | https://en.cppreference.com/w/cpp/concepts/integral
        
         | sitkack wrote:
         | Not related to your post, but it was marked dead, then I looked
         | at your comment history and didn't notice anything, but many of
         | your comments were marked dead. I "vouched" for this one. It
         | looks like you angered someone and they have been flagging all
         | of your posts.
        
       | WalterBright wrote:
       | I added 0b binary literals to C++ back in the 1980s.
       | 
       | https://www.digitalmars.com/ctg/ctgLanguageImplementation.ht...
        
       | alphanullmeric wrote:
       | Hey rust - check it out. Compile time evaluation without macros,
       | isn't that neat?
        
         | masklinn wrote:
         | You mean, like https://doc.rust-
         | lang.org/reference/const_eval.html?
        
           | alphanullmeric wrote:
           | That's nothing compared to what Constexpr and other c++
           | features let you do. The conditional compilation example for
           | instance.
        
             | sitkack wrote:
             | Zig has shown the true power of constexpr in being able to
             | provide a powerful construct that generalizes well.
             | 
             | After sum types (and pattern matching), constexpr is the
             | next great language feature that will be everywhere soon.
             | 
             | I would love for Rust to fully adopt constexpr.
        
               | alphanullmeric wrote:
               | It's sad that rust programmers will mumble about safety
               | every time you point out that rust lacks basic features
               | of other languages that would not affect safety at all.
               | Named arguments and default parameters is another one. Or
               | variadics.
        
               | tcfhgj wrote:
               | Yeah, every time... and right here you see the meaning of
               | 'every time'
        
               | qwertywert_ wrote:
               | Basic features? This took c++ a long time to get this
               | done. I like c++ but not sure what is the point being
               | negative about other languages would you prefer no one to
               | try new things?
               | 
               | They would accept new proposals for these if you want to
               | contribute, or just continue complaining.
        
               | ojosilva wrote:
               | Named arguments in statically-typed languages is a
               | naturally a hard thing to implement.
               | 
               | When used in code, it also results in many folding API
               | branches such as Foo::new(port=8088, host="localhost") or
               | Foo::new(pipe="./myfile") - Rust preferred idiom here
               | would be Foo::new_listen("localhost", "8088) or
               | Foo::new_pipe("./pipefile"). So at this point idiomatic
               | Rust is preferred.
               | 
               | Also named parameters amplify your API naming fail
               | surface (think misspellings!) beyond structs and impl
               | functions... Like in "max_connexions" (sic), which is
               | actually an unintended typo in one of the RFC proposals
               | for naming parameters in Rust! It's hilarious.
               | 
               | Default parameters: my_port.unwrap_or(8080)
               | 
               | Variadics: useful, but needs to be carefully considered.
               | There are workarounds now in nightly for C compatibility.
               | There also concerns about how "..." and ".." syntax play
               | out and incompatibilities on different implementation
               | targets. And, yes, safety issues.
               | 
               | It's not that Rust people will mumble about safety. It's
               | that it would be stupid to design a language all around
               | safety and then implement every cool language feature in
               | the world today without scrutinizing them for safety
               | issues.
        
               | alphanullmeric wrote:
               | 1. You don't have to use those features if you don't want
               | to.
               | 
               | 2. Every other modern language makes this work, only rust
               | makes excuses.
               | 
               | 3. Rust already has default generic type arguments and
               | "named parameters" for struct constructors (which in fact
               | are _mandatory_ ), for which the same criticisms also
               | apply to.
               | 
               | Rust is already tedious enough to work with as is, and
               | the last thing it needs is more builder pattern or manual
               | name mangling. You already need mut/non mut for getters,
               | sync/async, etc. just look at [serde](https://docs.rs/ser
               | de/latest/serde/trait.Deserializer.html) or polars or
               | ndarray. Ugliest language ever.
        
           | fooker wrote:
           | Not even close
        
         | paulddraper wrote:
         | Ssssh...the HN Rust Brigade will hear you
        
       | jrmg wrote:
       | I had some fun recently using (abusing?) `constexpr` to process
       | string literals at compile time to 'compress' then in the binary
       | and save a few bytes in my microcontroller.
       | 
       | https://gist.github.com/th-in-gs/7f2104440aa02dd36264ed6bc38...
       | 
       | I'm just shaving some bits off - but I guess in principle you
       | could do anything that's `constexpr` evaluatable. Gzip
       | compression of static buffers?...
       | 
       | Godbolt example:
       | 
       | https://godbolt.org/z/qc7jhKoGc
        
         | Gibbon1 wrote:
         | It's a little insane they don't have run length encoding for
         | statically initialized data.
        
         | mhh__ wrote:
         | This is the intended purpose of the feature. gzip may be a
         | little aggressive given that you have to unzip it again at the
         | other end but its very possible (and potentially not even as
         | expensive as one might expect given that these types of
         | compression algorithms can be tuned on a speed/size tradeoff
         | and backoff when struggling to compress).
         | 
         | https://github.com/PhilippeSigaud/Pegged is a D library that
         | generates a parser generator for you based on a grammar
         | (string) at compile time.
        
           | mikepurvis wrote:
           | I think a lot hangs on what the data is ultimately for-- if
           | you have twenty compressed blobs and you only need one of
           | them at a time, then it's perfect, or maybe if you have a
           | single large blob that you just need targeted random access
           | to.
           | 
           | But if you're going to uncompress it all right at startup,
           | than it's not worth it at all-- microcontroller flash is much
           | cheaper and more plentiful than RAM.
        
       | c7DJTLrn wrote:
       | Not a C++ developer but nodiscard is gross in my opinion. I've
       | seen codebases littered with it for no good reason. Why should
       | you care if the caller uses the return value or not?
        
         | Blackthorn wrote:
         | Honestly it's kind of useful as a customized warning to
         | callers. Callers can still ignore it by, for example
         | (void)funcall_with_nodiscard(args). But they have to explicitly
         | declare the intent to ignore the result. That seems like an
         | all-around fair construct.
        
         | frozenport wrote:
         | Forces your coworkers to check return codes
        
         | jwitthuhn wrote:
         | When the return value can indicate that an error occurred, the
         | caller can only know the function actually succeeded by
         | checking that value.
        
         | halayli wrote:
         | So that they don't misuse it. A good example is vec.empty().
         | There's no point calling vec.empty() without checking its
         | return value. It helps distinguish noun vs verb.
        
           | [deleted]
        
         | i-use-nixos-btw wrote:
         | > Why should you care if the caller uses the return value or
         | not?
         | 
         | Nodiscard is a way of "enforcing" a contract with the user
         | about how your code needs to be used in order to avoid
         | undefined behaviour.
         | 
         | (I say "enforcing" in quotes because, instead of being an
         | actual constraint, it's merely an attribute - so a conforming
         | compiler can happily ignore it)
         | 
         | Here are two examples of where it is useful:
         | 
         | - Returning a success code that must be checked before
         | proceeding with an operation that could have bad consequences
         | if the previous operation failed. Unchecked operations are one
         | of the major sources of bugs in the wild, so no discard at
         | least points the user to the potential problem.
         | 
         | - Returning something that doesn't make any sense to
         | immediately discard. This is usually down to a mistake - such
         | as calling vector::empty thinking that it's going to clear the
         | vector, when it actually returns a bool telling you if it's
         | empty or not (an awful name, but then so is vector...). It
         | makes no sense to check if it's empty without using that
         | result, so the warning indicates that the user has made a
         | mistake.
        
         | malkia wrote:
         | There are many good uses, and I wish the `new` always required
         | it.
        
       ___________________________________________________________________
       (page generated 2023-05-29 23:00 UTC)