3 ms·
I've been dabbling in embedded programming. Everything is written in C. I just don't understand why. C++ solves pretty much all problems if you want it too (RAI
by yau8edq12i 3y ago
I've been dabbling in embedded programming. Everything is written in C. I just don't understand why. C++ solves pretty much all problems if you want it too (RAII, smart pointers, move semantics) and the frameworks writers wouldn't need to implement their bespoke OOP system on top of opaque pointers and callbacks.
Maybe it was bad luck on my part, and other embedded frameworks are better; but I got into both ESP32 and STM32, both frameworks are the worst spaghetti code I have ever seen. You need to jump through at least one, often two layers of indirection to understand what a particular function call will do. Here's an example of what I mean:
// peripheral_conf.h
#define USE_FOOBAR_PERIPHERAL 1
// obj_t.h
#define USE_OBJ_PARAM2
// In the library header
#ifdef USE_FOOBAR_PERIPHERAL
#define DoSomethingCallback FoobarCallback
#endif
// foobar.h
status_t FoobarCallback(int32_t data, int32_t param);
// obj_t.c
status_t Init(Obj_t* obj) {
obj->param1 = obj->init.initparam & 0xFF;
#ifdef USE_OBJ_PARAM2
obj->param2 = (obj->init.initparam >> 16) & 0xFF;
#endif
obj->callback = DoSomethingCallback;
return OK;
}
status_t DoSomething(Obj_t *obj, int32_t data) {
#ifdef USE_OBJ_PARAM2
return obj->callback(data, obj->param2);
#else
return obj->callback(data, obj->param1);
#endif
}
// main.c
Obj obj = {0};
obj.init.initparam = 0x12345678;
Init(obj);
DoSomething(obj, 0x42);
And that's an easy example. Macros everywhere, you need to grok what's happening in four different files to understand what the hell a single function call will actually do. Sure, the code is super efficient, because once it's compiled all the extraneous information is pre-processed away if you don't use such and such peripheral or configuration option. But all this could be replaced by an abstract class, perhaps some templates... And if you disable stuff you may not need (RTTI, exceptions) then you'd get just as efficient compiled code. It would be much easier to understand what going on, and you wouldn't be able call DoSomething on uninitialized data... Because you'd have to call the constructor first to even have access to the method.
Anyway, thank god for debuggers, step-by-step execution, and IDEs.
- outsomnia 3y ago> But all this could be replaced by an abstract class, perhaps some templates... And if you disable stuff you may not need (RTTI, exceptions) then you'd get just as efficient compiled code. Isn't it just that your personal in-head GPT has been trained on C++ and wants to see it everywhere? It's not so easy to make very small embedded implementations and there's a reason after 25+ years C++ has not made inroads there.
- yau8edq12i 3y agoI'd appreciate if you made your point in a less condescending and dismissing manner. Anyway. No, C++ is not even my programming language of predilection. Not sure why you would make assumptions about my background while knowing nothing about me. But I can recognize OOP patterns when I see them. There's even a book about that https://www.cs.rit.edu/~ats/books/ooc.pdf https://www.cs.rit.edu/~ats/books/ooc.pdf C-styled OOP is not a new concept. C++ just does it better. The reason C++ has "not made inroads" may just be inertia, you know. And look at Arduino - if C++ code can run on an 8bit ATmega MCU, it can run anywhere. The whole language is designed around "pay for what you use and nothing else".
- JonChesterfield 3y agoC doesn't need to look like this. Some of it does, because it comes from the days where function inlining and dead code elimination were aspirational, but your C compiler is probably derived from clang or gcc now and totally capable of folding away branches on constant data. An abstract class is a struct with function pointers in it. Mark the fields const and the instance const and it'll be devirtualised and optimised away. If you miss overloading, `static inline __attribute__((overloadable))` wrappers in a header will bring it back. Code generators can be better for debugging than built in templates. At the source level they look the same, but if it's behaving weirdly, you can look at the generated C instead of the templated layer. C code can look rather like modern C++. If you're up for feeding it to a custom preprocessor to implement templates, or especially if you've gone as hardcore as the compiler front end under discussion here, C++ starts to look a lot like a syntactic obfuscation over C. [it's not quite syntax over C, the languages play divergent games with semantics as well, but picking a different set of syntax abstractions over C to the C++ one is an interesting way to go]
- yau8edq12i 3y agoI can't say I understand the overall point you're trying to make.
- JonChesterfield 3y agoYou're working with embedded C that is a rats nest of macros. It could instead be sanely factored and readable C without the macros if it was written with slightly more trust in the compiler.
- yau8edq12i 3y ago...and you're willing to use nonstandard attributes, code generators, and a custom preprocessor. At this point, why not C++, seriously? And can you show me an example of an existing framework for embedded device that's not the spaghetti code I showed?