The speed at which you can write great C code often far outstrips other languages which are applicable to the problem domain.
All languages are a compromise, there are no silver bullets.
Overall, it is still far more portable than C++ or any other new language.
Heck I learned to program in C++, back when DR-DOS 5 was the latest version, and all I had available was 640 KB to play around, leaving aside MEMMAX.
Nowadays the only reason many embedded developers keep using C is religous.
I think you're making some extraordinary claims. I'd love to see some receipts. :-)
I have had trouble compiling older C++ code bases with newer compilers, even when specifying C++98 as source standard. I gave up trying to get Scott McPeak's Elkhound C++ parser to compile, last I had to attempt it.
C is a bit more forgiving on that topic (it hasn't changed as much, for better or worse).
Still using tcc, or stuck in GCC 5 ?
There are quite a few besides various PICs AFAIK, how modern they are is subjective I guess, and it IS mostly the weaker chips. Keil, Renesas, NXP, STMicro (STM8 MCUs used to be C only, not sure today) all sell parts where C++ is unsupported.
> Nowadays the only reason many embedded developers keep using C is religous.
I don’t completely agree, but I see where you are coming from.
The simplest tool that gets the job done is often the best IMO.
In my experience, it is much more difficult to learn C++ well enough to code safely, compared to C.
Many embedded developers are more EE than CS, so simpler software is often preferred.
I know you don’t have to use all the C++ features, all at once, but still :)
Horses for courses. I prefer C for anything embedded.
#define maybe(T) struct maybe_##T { bool ok; T value; }
#define maybe_just(T, x) (maybe(T)){ .value = (x), .ok = true }
#define maybe_nothing(T) (maybe(T)){ .value = (T){ }, .ok = false }
#define maybe_value(T, x) (({ maybe(T) _p = &(x); _p->ok ? &_p->value : (void*)0; }))
Please compare this to your other languages.
Especially the the safety is something I can't step over. I don't feel it offers anything substantial over just having the struct without these macros.
1. Ergonomics: Forcing the null sanitizer is quite a sledgehammer you often cannot, or don't want, to pay, You are forcing global behavior for something you really only want locally for this construct. A misuse of maybe_value is a crash where in other languages you have case/match and compile time errors. There is no type inference so you have to be explicit at every, single, line, that you really mean a maybe(int) or whatever.
2. Integration: No libraries uses this, meaning any usage is limited to your own code only. Requiring manual and error prone translation at every interface.
3. Safety: No check the null sanitizer is actually on. This is a huge footgun where someone thinks "I know I use this neat macro I saw here". And then of course not enabling the null sanitizer and everything breaks. So now for this to be safe it's not enough to understand the code. You need to check if the compile options are just right. This is especially insidious since this cannot be hidden in some object file where you make damn sure the sanitizer is on. the CONSUMER of this API must enable the sanitizer
For these reason I would ban this macro in any code I have control over.