[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)