7 ms·
C++17’s useful features for embedded systems
- tonetheman 3y agoWhat 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(); } }
- sitkack 3y agoNot 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.
- Leherenn 3y agoHow do you call it and what is your error message?
- secondcoming 3y agoLike this maybe? https://godbolt.org/z/6fnhT6h9v https://godbolt.org/z/6fnhT6h9v
- tonetheman 3y agoYeah 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.
- secondcoming 3y agoIt should work in C++17 too, the compile settings are just left over from something else I was doing on CE.
- Thorrez 3y agostd::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 https://en.cppreference.com/w/cpp/concepts/integral
- mmebane 3y agoIn this case, I think it's a typo, and should be std::is_integral: https://en.cppreference.com/w/cpp/types/is_integral https://en.cppreference.com/w/cpp/types/is_integral
- cmrdporcupine 3y agoIn-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 https://github.com/rust-lang/rfcs/issues/1756) but went nowhere.
- gumby 3y ago> 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`).
- AlotOfReading 3y agoI 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 3y agoHaving the compiler deduce the return type of a function depending on the constexpr path taken is really useful.
- mhh__ 3y ago`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.
- jcelerier 3y ago> `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__ 3y agoThis 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.
- Espressosaurus 3y agoconstexpr 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__ 3y agoDoing 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.
- mhh__ 3y agoWrt 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 3y ago[deleted]
- addaon 3y agox86 (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).
- jeffbee 3y agoTrue, 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__ 3y agoI'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.
- cmrdporcupine 3y agoIt 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.
- shadowgovt 3y agoSo much of modern c++ is trying to get around just using the preprocessor.
- gumby 3y agoThe 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 3y agoYeah, but they're trying to replace it with more botch. "If constexpr can be used to precompute statically-determinable logic, except except except..."
- ok123456 3y agoConstexpr 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 3y agoAnd for good reason?
- swader999 3y agoI'm sure other languages would have more than 17 useful features.
- smarx007 3y agoIsn’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)?
- jeffbee 3y agoYou 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.
- einpoklum 3y ago> 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 https://www.youtube.com/watch?v=LIb3L4vKZ7U
- BenFrantzDale 3y agoI think polymorphism has its place, and Lakos’s use of them for allocators is one such place: it lets you bind a well-defined interface to a concrete implementation at runtime. So rather than wrestle with templated allocators where every `std::vector` is a different static type, you can use `std::pmr::vector` and at a small runtime cost have huge runtime flexibility that (according to Lakos) can easily pay for itself.
- alphanullmeric 3y agoHey rust - check it out. Compile time evaluation without macros, isn’t that neat?
- masklinn 3y agoYou mean, like https://doc.rust-lang.org/reference/const_eval.html https://doc.rust-lang.org/reference/const_eval.html?
- alphanullmeric 3y agoThat’s nothing compared to what Constexpr and other c++ features let you do. The conditional compilation example for instance.
- sitkack 3y agoZig 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 3y ago[flagged]
- tcfhgj 3y agoYeah, every time... and right here you see the meaning of 'every time'
- qwertywert_ 3y agoBasic 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.
- kramerger 3y agoWhy 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.
- WalterBright 3y ago> I would rather have > uint8_t b = 0b1111_1111; In D, you would have: ubyte b = 0b1111_1111;
- dxuh 3y agoI think you can't do that, because the underscore may start a user defined literal suffix.
- WalterBright 3y agoD doesn't have a special syntax for user-defined literals, which avoids this problem completely. One can use templates for user-defined literals, such as: km!1000 for 1000 kilometers. Here, km is a template that takes an integer argument: struct Kilometer { int v; } template km(int V) { Kilometer km = Kilometer(V); }
- cmovq 3y agoIt 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 https://en.cppreference.com/w/cpp/language/user_literal [2] https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3499.html https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n34...
- raxxorraxor 3y agoDidn't know about this language feature. Seems neat. The ASM integration is also neat. If you compare that to C... Still, I have rarely seen C++ compilers for embedded systems. Although the latter definition more and more includes PC hardware.
- jrmg 3y agoI 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/7f2104440aa02dd36264ed6bc38ce553#file-packedstrings-h 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 https://godbolt.org/z/qc7jhKoGc
- mhh__ 3y agoThis 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 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 3y agoI 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.
- Gibbon1 3y agoIt's a little insane they don't have run length encoding for statically initialized data.
- dnedic 3y agoThe trick is to not even have strings in the binary. Take a look at trice, defmt and logging in ESP-IDF.
- deleted 3y ago
- WalterBright 3y agoI added 0b binary literals to C++ back in the 1980s. https://www.digitalmars.com/ctg/ctgLanguageImplementation.html#binary_constants https://www.digitalmars.com/ctg/ctgLanguageImplementation.ht...
- notbeuller 3y agoI really appreciate the single quote allowing for alignment: case 0b0001'0000: case 0b0101'1000: (edit, fixed formatting, but I also wanted to agree with a lower down comment asking for 0b001_0000 instead; a better choice.)
- WalterBright 3y agoAs you say, in D it's: case 0b0001_0000: case 0b0101_1000: which indeed looks much nicer. I have no idea why C++, when copying the feature, didn't use _. P.S. I copied this feature from 1983 Ada. AFAIK, it was a completely forgotten feature until D had it, then other languages started copying D.
- nuancebydefault 3y agoThe 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.
- jnwatson 3y agoRust. The alternative is Rust.
- nuancebydefault 3y agoI hope you will be proven right!
- marsven_422 3y ago[dead]
- GuB-42 3y agoI don't think Rust has an equivalent to C++17 constexpr, it is more limited. Zig may be a better option when it comes to compile-time evaluation.
- pie_flavor 3y agoThe `if` part of the quoted example, you can do in Rust; the `else` part is coming with the specialization feature soon. Everything you can't do with traits, including specialization, you should probably not be doing; modern C++ tends to be an incomprehensible template forest.
- staunton 3y agoEspecially 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...
- c7DJTLrn 3y agoNot 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 3y agoHonestly 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 3y agoForces your coworkers to check return codes
- malkia 3y agoThere are many good uses, and I wish the `new` always required it.
- jwitthuhn 3y agoWhen the return value can indicate that an error occurred, the caller can only know the function actually succeeded by checking that value.
- i-use-nixos-btw 3y ago> 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.
- deleted 3y ago[deleted]
- keithnz 3y agoNot really sure they are that useful for embedded systems. I think the most useful thing is in 20, and that is coroutines. For bare metal embedded systems this simplifies a lot of things. But isn't super common on embedded toolchains yet.
- cyber_kinetist 3y agoI’ve heard coroutines in C++20 were dead on arrival (especially on embedded) because it may liberally do heap allocations to store locals and there’s no way to control this. (Typical library implementations I’ve seen for lightweight coroutines/fibers seem to set the stack size for each coroutine as fixed…)
- ComputerGuru 3y agoOh what a shame! I just replied to the parent comment (before seeing your reply) about how incredible async/await “coroutines” were with embedded rust requiring actually zero runtime cost (no alloc, no runtime, no dynamic scheduler).
- csb6 3y agoYou can provide a custom implementation for "operator new" for the coroutine, so you could instead use some sort of preallocated buffer to store the coroutine frame (or some other custom memory management scheme), but yeah the design assumes that sometimes there will be a need to stash the state of the coroutine somewhere.
- ComputerGuru 3y agoAsync/await in no_std rust was a godsend for embedded firmware and driver development, turning really simple and linear code into a tiny state machine with zero alloc and no runtime cost. I can imagine how coroutines could provide similar for C++. I migrated a rust LoRa driver from traditional blocking to async/await in order to test two modules in simultaneous send/receive mode as part of the test suite on a single STM32 chip. Aside from pleasing the HAL abstraction by bubbling all the generics throughout, it was an entirely pleasurable experience and made much better use of available resources than the traditional approaches without having to manually manage state across yield points or use a dynamic task scheduler. No OS, no runtime, no manual save/restore of stack, no global variables. It is really the future of the truly micro barebones embedded development.