* No exceptions, ever
* No RTTI, ever
* Compile with -Os
* Limit STL to the non-allocating, non-throwing parts
* Don't allow heap usage
* Link against the C standard library so you don't get accidental heap allocations from lambdas with large capture specs and static construction and destruction fail to link
* Use alternatives to std::function (which while easy is very large and very slow)
* Limit the use of virtual functions (some people suggest eliminating them altogether but I feel like that's a bridge too far).
* Avoid inlining functions if you're not sure that they reduce down to something trivial. I've saved a kilobyte or two by moving some particularly large, commonly used functions into separate translation units so that the compiler couldn't inline them.
* Look at and track your space utilization commit by commit. If the data segment goes up unexpectedly, revisit what you're doing. You may have unwittingly allocated a big block for something. Similar for the code segment.
You get used to the constraints. Type safety, templates, and compile time programming make it 10 times better than C for the purpose in my experience. The only reason to use C is if your C++ compiler is garbage (which is often true for the tiny low powered processors). But if it's ARM you probably have access to a modern compiler and the only reason to stick with C is inertia.
I've been programming with embedded C++ for ten years and every time I have to poke into the C part of the codebase I end up hating life.
Lambdas themselves never require heap allocation. I guess you meant std::function?
Also, in a real-time system context, exceptions can be undesirable since they might cause non-deterministic behaviour.
Catching an exception can be surprisingly costly. Did some benchmarking a while ago on the embedded, real-time system I work on and saw that throwing and catching a std::runtime_error had about the same execution time as a rather slow CRC32 calculation (no pre-calculated tables, no special instructions) of a 256 bytes input array. (Of course, this depends a lot of the CPU architecture, compiler, etc.)
This is in contrast to python or Swift for example, their standard libraries are more “throw-prone”. Building off the previous example Swift’s String.init(contentsOf:encoding:) throws on error on failure.
So in practice, IMO it is usually safe to disable exceptions in C++. Though, I have run into tricky ABI breaks when you link multiple libraries in a chain of exceptions->noexcept->exceptions and so on! You’re of course at the mercy of nonstandard behavior so buyer-beware. I definitely wouldn’t advocate for turning them off -just- for a binary size reduction.
You're just going to end up with an insane amount of error handling only to discover that in the real world, there's likely nothing you can really do anyway.
Using memory that's been allocated but not committed seems like a recipe for disaster.
It can greatly accelerate sparse datastructures.
With exceptions disabled you still get a panic (e.g. the program immediately terminates) in places where an exception is thrown, this should be at least as safe as having exceptions enabled.