For any embedded programmers, I highly recommend Bob Martin's popular book "Clean Code". It guides you in a similar direction to what Dave is showing here, but goes into detail about why it matters and specific steps on how to get there. Being a EE, I never encountered these ideas in school, so it was very valuable. As others have pointed out, the C code on the right is not really a fair comparison. While functional, the C code still needs to be cleaned. Your main C loop (top level of the application) could look something like this: if (isButtonPressed()) turnLedOn(); else turnLedOff();
This is an unfair comparison. Yes, ultimately you can achieve a higher level of abstraction with C++ compared to C but what you are doing there is comparing highly abstracted C++ code with almost non-abstracted C code. It's not so much a language problem but rather a failure to apply the same level of abstraction on both sides of the equation.
pcbreflux Only once, plus C will never give you a compile time error if you set up a port incorrectly, if ports are in conflict etc. I would recommend you to watch the talks by Odin Holmes and have a look at Kvasir MPL and Kvasir Bit library to see the true difference. This is a bit of a contrived example that did not take the full power of TMP into use, as if the initialization code for multiple pins are the same, the register writes can be merged at compile time, giving both flash and speed benefits.
Emil Fresk Sure only once and no doubt more abstraction have clear benefits, but showing only a tiny portion of the code remain a unfair comparison and confuse beginners or "managers".
Indeed, basing anything on one piece of information is not a good strategy. However hopefully it will increase awareness and help the state of the embedded industry today.
I actually prefer the C way, because I can see exactly what is happening. Sure, I can just put everything in some function in a different file and get the same result, but even for C++ the constructors have to be somewhere in some library. It comes down to how abstract you prefer your main program to be.
Yes, please go into depth on this. Something like 45 minutes explanation of the Pin and Led classes along with the template part would be highly appreciated.
I'm sure this video took some time to produce, but it wasn't a good time spent. It has virtually no sense to compare semi-standard library from a manufacturer in C to a custom library in C++. It's not a comparison between languages, it's a comparison between libraries. I it surely possible to create a library in C with a similar interface, obviously not using "methods" or overloaded operators, but the same amount of code should fit in around 10 lines of C code.
I thought about this and wasn't sure how it could be done in that manner in an elegant and internally consistent way. The library here exposes all of the functions that the manufacturer library exposes, the only difference is type safety and automatic management of port clock power. I'd be interested if you had any ideas, I don't like making objects when a function will do but classes are just so powerful.
David, don't know if this would be of interest to you, but how about a lesson on timing constraints of FPGAs, how and why to specify them, impact on propagation, layout, and upper end clock speed, and such? I mention it because it seems a really cool topic, the importance was emphasized in my graduate reconfig computing class but nothing practical was provided.
I would love too, super interesting but I'm not ready for this topic yet. Its super hard to find good information on that stuff, I've found some RF theory is pretty applicable so been looking into that, not getting anywhere fast.
I stopped using assembler over 30 years ago. Yet so many people still use it because it is 'fast' or some other arcane reason. C++ which itself is decades old shows how code can be simple, fast, understandable and supportable. The question is will you release your libraries for others to play with? Otherwise we get everyone reinventing the wheel. Yet again.
Yes of course! Its part of the uSupply code, but I need to discuss when to release as much of that project is still secret squirl :) I can release my Pin code I think, i'll check on Monday.
It depends on the situation, about 0.5% of the code I write is assembly language. It's for specific pinch points. While compilers are smart, they don't know anything about your data, so they make broad assumptions. It's not uncommon to be able to double the speed of your code if you can optimise your algorithm to make maximum use of on chip resources like registers rather than going off chip for example. It's rare, but assembly language still has its place. More common is to be able to read assembly language while debugging, that is a very useful skill. More fundamentally if your algorithm is not designed for speed, no amount of assembly language will save you!
Yeah that happens, I would recommend checking out compiler directives for particular blocks of code but assembly for certain blocks can do the job too :) Nothing does nop like assembly ;)
I've been working with visual studio for years now, and just now (thanks to this video) I realized you can select columns with the cursor by pressing Shift+Alt. Thanks!
FWIW, there's really no reason the C code has to be any more complicated (all told) than the C++ code. It's just that you're using a very low-level library of C code and a higher-level library of C++ code. You could build a higher level C library and the code wouldn't look hugely different (you'd have to call a function to set the LED or Enter output, whereas in C++ you presumably use assignment).
> there's really no reason the C code has to be any more complicated define complicated? easy to read? maintainable? there is, the reason is C, C has few abstractions, C++ has "all the abstractions", RAII alone is a huge productivity, and usability boost, you can't get that with C, because C has no language constructs to control lifetime, the equivalent C code would not just be more verbose but error prone, and there is no way around it
Wow this is awesome. Right now at work we are getting further with our prototypes, and i'm doing this exact transition with our code from the brute force C code, to the much more usable C++ version.
1) You have to mention, that LED is a variable that occupies some space in RAM whereas C code occupies RAM only for the initialization part. It might not be critical for this case, but it might be critical piece of information for somebody, who decides to use your advice; 2) You have put data into the type declaration instead of the instance of the class (struct) yet an instance of your type Pin still occupies some space in RAM even though it holds no state - that is clearly not a good practice from a C++ design standpoint. 3) Since you are encoding state into a type declaration, what do you think will happen, if you define a new variable with the same type? You will get two variables referring to the same pin yet wasting the double amount of RAM to hold both variables; You could have defined a const variable in the global scope, but then your fancy operator overloads would look even stranger; 4) Now lets look at your assignment operator overload - you have a variable that holds no state, instead the assignment operator changes the global state (the GPIO pin); What if you assign one variable of the same Pin type to another? It doesn't do what a typical programmer would except it to do, because the assignment operator doesn't transfer state of the variable but rather copies the global state onto itself. This is mind boggling and is not a good C++ programming practice as well. While it is encouraging that C++ is squeezing into the embedded space (not so long ago the C had a very similar fate trying to compete with assembly language) yet we are not there yet. Making you code not just shorter, but also to obey basic principles of C++ design would require quite a lot of effort. And we are talking only about GPIO pins here. So, maybe there is a reason, why the embedded world is still holding to C and assembly language - these languages are perfectly solving the problem w/o having to dwell into the wonderful world of metaprogramming and building of multi-layer abstractions. Edit: I just want to add that GPIO pins are very similar animals, but using your technique it is not possible to group them or to put into a collection, which is also something to think about before taking you advice as it is presented.
firstly sizeof(Pin) == 0, you seem to think I'm storing pointers in each Pin class or something... It is a purely static object (Because physical pins are purely static). It doesn't hold any variables, it connects directly to the registers. My pin operations are equivalent to RAW register operations, only difference is that all the shifts n whatnot are automatic. Depending on compiler flags it can store some global statics, one per unique pin type. Pins are static and by their nature singleton. It is a compile time error in my configuration to assign one pin using another pin, as I believe it should be. You can assign state of one pin to another etc but is must be explicit or use operators which infer what is needed (boolean operator). Not everything at once, assignments must be explicit and typed. If you wanted a copy of the pin, simply use the type, it is singleton as it is intrinsically linked to a set of registers. It is an approach which avoids global and allows local pin aliases without using references or pointers. The type is a short cut to access individual registers, not all of them at once (except in construction). If you wanted to assign mode of one pin to another this is valid: Pin p1; Pin p2; p1 = (Mode)p2; Not all pins have the same capabilities, assignment of a bunch of state that might not be possible is silly and I set out to remove a bunch of runtime checks that other libraries require. Additionally templates fold in various ways that don't double the binary size, sometimes it increases binary, sometimes it doesn't. This type of behaviour is propagating to many compilers now days its called COMDAT folding in MSVC, gold in gnu icf. Additionally, template functions are only instantiated when called, unused functions do not enter the binary.
The great thing about C++ is that I have to ask "what operation are you performing when you type 'p1 = (Mode)p2;'? I mean, you can type that, but I have to go digging around through layers of templates and classes to find out what that code means, because it's not even slightly obvious what you've done there. In C it would be, but in C++ I'm just guessing that by that syntax you're forcing a polymorphic object to surface as a particular class so you can copy just those attributes onto another object?
Under the hood it detects the type you are trying to assign to it and runs the appropriate direct register set/clear to the bit needed. There is no inheritance or the like, and no copies of operator parameters either if I did the forwarding correctly... Nope, just assign to it with an enum related to pins, the compiler will tell you if its invalid, although all pin enums are valid assignments here.
follow up question: try working a button and a LED with the sample GPIO i.e. GPIOA and compile both C & C++ compare and see what actually happens. Along the same case, expand the same situation with more complex cases, there are no doubt C would do a better job.
Ok, so long response. You've opened a complicated can of worms. Generally, to avoid overhead in C++ IMHO for embedded systems: 1. Avoid heap allocations if possible 2. Don't use virtual functions 3. Use the standard library unless they conflict with the first two laws (many do) 4. Know your compiler flags* * If your not using exceptions, disable them, and for the love of all things don't compare unoptimized C++ (which is for easy debugging) to code with compiler optimizations. C++ is not designed for production use without the optimizer. ----------- There are certain things in C++ which can incur a performance penalty, virtual functions, the most widely spread issue IMO. Especially when people started believing all classes need a virtual destructor, this is wrong. ----------- But there are also things that result in improved performance, these being: --- constexpr functions to calculate complicate register settings at compile time without resorting to magic numbers or runtime calculations. --- Some level of type safety without needing to keep runtime type information, which means certain things do not require runtime checks to ensure proper operation. --- Things like constructors and private members can also provide similar guarantees which enable all member functions to avoid checks which simply aren't possible due to the nature of the object. --- More effective abstraction allows programmers to look for optimizations instead of being bogged down in micro-optimizations. Large structural issues are easier to see when properly abstracted in my opinion. Micro-optimization is similarly easier when isolated. ----------- There is also a myth that templates bloat code, they simply do not (well they do if you go nuts), but of course just like inline in C they can be misused. The mantra of the founders of C++ was "you only pay for what you use". Some people think that C++ is slower, it isn't, but use it badly (like all things) and it is. It takes quite some experience to learn to use it well. I maintain a large library (couple hundred thousand lines maybe I don't know) in C++ which is mostly performance focused, I spend large chunks of time benchmarking various algorithms (you can use C code in C++ programs) much of the library is for embedded systems. I have a lot to learn about this but I am reasonably competent here.
Your explanation is good. However, in this case you can only reply on the compiler, which in many case are not easily be optimized. C with ASM always works well in cheap production industry. As you said, i do agree that working with the "Correct settings" in C++ is an alternative to C. From heart, how do you expect a rookie would have the same amount of experience.
The C++ code on the left was not platform agnostic since it has "Port::B, 7" in it. On a Raspberry Pi, you just have a pin number and don't worry about ports. On most microcontrollers you'd at least need to think about changing those pin assignments. I think it makes a lot of sense to just have a single pin number like the Raspberry Pi or Arduino environments, so you can put that single number in a macro or constant easily.
That has been my experience too. I suppose I never really got to that conclusion though, hadn't thought about it, just mindlessly opened manufacturers libraries without thinking like I expect everyone else does. External library bugs, the absolute worst!
Macros can have their own issues, but yeah inline functions sure :) Just a couple examples on my issues with macros: #define cube_volume(x) (x*x*x) int dimension = 1; double volume = cube_volume(dimension++); You'd expect the answer to be 1, but the answer here is 6. Or: #define run_two_functions(x) {f1(x); f2(x);} if (condition) run_two_functions(x); else run_two_functions(y); The above doesn't work because it evaluates to: if (condition) { };
Of course, like everything you need to know what you're doing, your first example is the classic one they tell you in class, any C programmer worth their salt would be more than aware and wouldn"t really have to think too hard about it. The second example is unlikely too, you use macros mostly to do simple aliasing and inlining to improve speed at a pinch point, calling functions is possible but unlikely except in the classroom or if you're a macro ninja writing obfuscative code. This is all about improving readability and maintainability, not making it worse! I can't think of a time I've ever fallen with your examples jn practice in 30 years of C programming. Even inlining has its challenges, there is no guarantee it will inline your code for example, and debugging inline functions often doesn't work well. My problem with templates is that the concept came in late in my career, so it's not something I'd naturally use. I also find the syntax of them far from natural, like it was bolted onto the language, which it was of course! Writing to a port for the most part shouldn't need the compiler to generate a function call at all in an embedded system that doesn't have an OS, it should generate code to do direct peripheral register access.
David, I don't think this is about C vs C++. One one side, you are using a nice GPIO framework, and on the other side, you are using STM's GPIO library. You demonstrated that STM's library is horrible, but this has nothing to do with C vs C++. You can have a sane GPIO framework in C, and you wouldn't have to worry about "all this". Or you can have a bad framework in C++. You have demonstrated that a good library will simplify the code and improve readability. I am in full agreement with this conclusion. But a good library has little to do with the language.
Some languages lend themselves to better abstraction, but yeah no doubt the STM library could have been better. Was a real world example, for a project we are working on, the STM library was available, and I wrote my library to avoid its verbosity.
While the 'unfair comparison' argument does have some merit, I can see your point. The problem I have is.. embedded. A C++ object library designed for one microcontroller not only isn't going to work for another micro, but it will be a PITA to convert. Even in the STM32 line, the pinouts and capabilities differ widely. Any good code should be well commented, and that goes double for code that will be seen by others, _triple_ for code you're going to make public domain. If your regular C example is similar to the C++ object definitions, they're not commented at all! If I were give the task to convert that code for a different microcontroller, I wouldn't. I'd study it enough to get the gist of the process and what variables and functions needed to be exposed, and write my own.
Personally I would much rather use rust than either of C or C++. Thankfully there is some work going on to make it more useable on microcontrollers and the like.
I've been involved in embedded development for over 18 years and have steadily been moving up the stack over that time. From assembly, to C to C++ and from bare metal to rtos. And I'm never going back. Look forward in the future to moving even further up to stack and being able to get more done in less time.
you can get around with overloading operator= instead of the Pin::Get and Pin::Set functions; having the pin port and number as template arguments, honestly feels like abusing the type system, I think they should be normal constructor arguments, it also going to make it hard to store Pin objects in a contrainer, as each Pin is a different type if their template arguments are different
No. You cannot assign to function results in C, one function cannot contain all functionality of the operator enabled class. The advantages of the class: - compile-time calculation of register offsets (so we can actually use the ports registers), - type safety, you cannot physically enter invalid pins or ports, which means *no runtime checks* needed. - Physically less code in the main function - Using standard operators - Full register exposure (all options are available) for all pins without flooding the global namespace with what would be 2 + 2 + 2 + 2 + 2 + 2 (12 functions or 6 if you want to sacrifice some speed) functions pin function. - Code is more expressive of programmer intent I believe as it takes less space to understand the same syntax while at the same time using standard grammer One function also requires runtime calculation of port offsets for the port registers or alternatively you need to maintain a platform specific pointer in your main application code but that is hardly portable as the platform specific structure isn't portable (similar to the ST library). Remember the class here exposes every function of the port, automatically manages the port clock, and provides type safety which prevents the need for any runtime checks (nullptr, invalid port index, invalid pin number etc). If theres a neat way to do it post it here, I'm always interested :)
I would love to see the speed comparison of both the codes as well as the total bytes consumed. When you are doing very advance level stuffs then I would imagine C++ would complicate the things. Thats why C was prefered choice for building operating system codes.
The total bytes should be the same or less. All the template stuff is just resolved at compile time. It's like #define in a type safety manner. And if you create advanced level stuff, C++ will help you to structure the code. Especially if you need to manage resources. For example, a lock class automatically releases the lock when the instance goes out of scope (you leave the function/method/case/loop-iteration where the lock was created). Also compile time functions can generate lookup tables without any runtime. And C is only preferred because all operating systems are old. Before 2011, C++ had problems solving certain problems properly. Since then much stuff has been changed. Look for C++ and embedded programming on youtube. You will find some lectures on the topic. As an example of modern C++, also a pong game for a C64 has been used. You can not do things in C as easy as in C++.
@@kla_sch3864 C++ is not intended for performance but yes OOP makes thing easier to manage. That's why C is not for all. in C++ memory footprint is much larger even for doing small things rest aside the speed performance.
They not "setting up a clock", they're enabling the clocking of the peripheral which is STMs way of power gating function blocks. It's actually important that this is verbose, I'd even recommend you check what your C++ code does because if it happens to be stupid enough to enable all ports even if you're just using a single pin, you're wasting (albeit a tiny bit of) power. Things like that also become very important if you want to use sleep modes. It might seem tedious and superfluous to a beginner but one you reach the more advanced levels of MCU programming having the control over tidbits like becomes an important affair. NB: I haven't looked at the C++ abstractions, maybe they offer fine grain control or not but at least it's good to know about this stuff.
Firstly, I like you, you clearly know what your talking about! Thanks for the comment, very relevant. But.... Nope you haven't given me enough credit here. It automatically detects what port is used (at compile-time) and enables the minimal number of ports needed. It also only does this once per port used, it doesn't even check a second time the port is used. It also can disable ports once no pins on that port are in use (out of scope).
Speed should be identical, abtractions in C++ don't incur penalty, memory potentially could be but I believe my template code folds so it doesn't add to memory overhead.
The C++ optimizer also wasn't covered, and this is the really cool part. The optimizer is the thing which goes around doing things like removing unneeded variables, inlining functions, removing entire objects and functions if they turn out to be unneeded. As you should know from the C optimizer, what you write isn't necessarily what you get when compiled. The C optimizer can do things like constant folding, remove unneeded variables and so on. The result of this is that quite often the code generated for C++ is comparable to the code generated for C. And this is common for PC applications, so why would there be any difference for embedded?
Might be a bit late, but are you able to post the assembly code of your LED = State::High vs the C function call to set the output high? I'd be interested to see whether it actually does fold down as far as you expect or if the compiler does something else :)
Dave, I'm really trying to be open minded here but I'm not sure how long lasting it is to use c++ on limited micros. I have a design I'm working on using the stm32f042k6u6, great chip,32KB of flash, lots of peripherals; But once I took STs MXCube code generator and It generated all the init code I needed, to my surprise just the init code took over 60% of the chips flash! Referencing an article from the embedded muse about how these APIs are usually written (Junior devs that write and then god know who maintains the code), it seems the best way was to rewrite the code on you own. going in deep you can see that the programmer that implemented the API wrote a lot of safety code that creates a lot of overhead: 1. passing large configuration structs around 2. asserts on parameters 3. spin locks for multi task codes Just a lot of code that is best implemented by good writing by the customer rather than the vendor. To make a long story short, My entire code including the init functions, working with an oled lcd, spi flash interface, i2c, USB,RTC, Timers, Interrupts and ADC only takes about 50% of the flash. While the my code is non generic (my functions assume single interfaces that you supply a pointer to externally, just to save code size.) it works great and no one died yet. Going into c++ might contain the same overhead that will quickly guzzle up the flash I barely have, So while blinking leds and reading buttons is nice, I'd stay clear from using a higher language for flash limited devices. Heck if it was an f4 or an f7 where you have a substantial amount of flash, I say go crazy with it, use C++; But for tiny Embedded Systems, I'm sticking with C.
It really is a function of the API, C++ doesn't intrinsically consume more memory but developers sometimes get carried away and can do things in their code which result in large code bloat. exceptions and virtual functions are two major things that really take memory usage but you by no means need to use them. If C works for you, thats fine :) I'm just showing what I do.
C++ can easily provide zero-cost abstractions, there is absolutely nothing inherent in the language that causes code bloat. If a project has code bloat, it's because of the code, not the language.
And with ASM web services can be easily created, there is absolutely nothing inherent in the language that causes code grown to overcomplicated piles of unreadable data.
JAVA can easily provide zero-cost abstractions, there is absolutely nothing inherent in the language that causes code bloat. If a project has code bloat, it's because of the code, not the language.
All versions of C are dinosaurs. Extreme terseness, low human readability, no data bounds checking and case sensitivity, all to minimize processor time on the puny 8-bit micros of the 1970's. Even if compiling code for running on a tiny processor it makes no sense to have a PC interface which behaves as if it was running on same. Why not use a more human readable syntax, with meaningful keywords instead of bracketbracketbracketbracketbracketbracketbracketbracketbracketbracketbracket, case insensitivity, and bounds checking so as to avoid security vulns arising? Something like 80% of all IT vulns are down to C/C++ buffer overflows. If 80% of deaths in road accidents were due to not wearing seatblets they'd make it mandatory to wear them. -Wait a minute, they have.
"all to minimize processor time on the puny 8-bit micros of the 1970's." BULLSHIT. C was designed for making Unix, and it does what it's designed to do quite well. C is the language of choice for writing operating system kernels. (With a sprinkle of assembly for interrupt management and such.)
Nice touch! Let's continue! Using pure C or C++ have sense in lot of cases, because programming MCU is not so trivial like standard programming. I'm using both because it's dependent on performance and control of your code. But off course you have to be professional to write good code in pure C.
Thx for your comparison. Eventually I would agree that C++ has nice advantages. BUT I think you but C into a unnecessarily bad light, as it's not a big deal to get the exact same two-liner in C as you have in C++. You can easily hide the definitions you show for the C example into a further abstraction, as it is done in your C++ example already.
Not all uC manufacturers have free C++ compilers available, it's probably relevant to the Cortex-M series but other micro's I prefer to write to the registers direct, be it in C or assembler - like PIC12 for example. All this video really shows is something you've written to abstract, you should have shown something in C and then the same thing in C++ without any custom layers.
True, even PIC18 barely has any options. This was made a a real example, real code, real device, real provided library (with slight modification for this video). I am a working engineer, its hard to justify making the same thing twice with the foreknowledge that half of the work is entirely redundant.
So, you could have just as easily created the same "library" with only 3 / 4 lines of code in C, just as well as you did with C++, looking at the library code, it's no different from the C counterpart, you just chose to show the library calls for C++ and the GPIO calls for the C example, so in reality, not a fair comparison?
Yes, but somewhere you had to code the classes. You don't get the class (and its code) for free. So, if you made a mistake when coding the class you wind up with the same issue as making a mistake in the C code. In fact, there are no real savings here in coding in C++ vs C. He is using the generated code in the C example and he had to do that for the class in the C++ example (or write his own register and bit access code). I could just as easily create a function in C that handled all the details like a class. Then I could make it as pretty as the C++ example. So no savings in the amount of code, and no savings in what I have to remember when setting up pins. Realistically, if all you are doing is blinking an LED and some other simple tasks, then the STM32 processor and the HAL are way overkill. You should just use an 8-bit microcontroller. Or the LL drivers for the STM32, or to really simplify it just code to the registers and set up your own functions to access the registers instead of the excessive layers of the HAL/LL. What is being shown in the C++ example is very misleading to new embedded engineers. There is a ton of code in the class doing the same thing as the C code but it is hidden, which you can do in C. I am not against using C++ in bare-metal embedded firmware, and it can be just as small and just as fast (I did the testing and found a 4-byte and 2-instruction cycle difference between the C++ and C code due to the constructor call which is 1 address added to the stack and 1 branch). Yes, I like that you can hide the details in C++, but what I have a problem with is in this example the details could have been as easily hidden in C. This is a very misleading way to say C++ is better than C. They can produce the same code and both can look as simple as the C++ example. Coding the class is just as difficult as the code he shows for the C example. I say show us the class code.
Hey David, I don't fully agree with the examples that you've given, for that well, managing pins has always been a problem for me. Sure, STM32s and ATMELs, if I'm not mistaken, you can control the pin directly on peripheral. On some TI chips you need to configure the pin pad, and then the gpio peripheral, if not some other control structure crap. Now, I do agree on the use of C++ on other topics, such as the numpad (yes, to use that you need some sort of pin abstraction). That is somewhat Arduino is doing (after some articles online, I've concluded that they are/were doing it badly). I think that the strongest point that C++ has to offer to the embeded community is not the abstraction layers, at least not in the first features. Namespaces, structs/classes, inheritance and as mentioned by others here in the comments, the optimization that the compilers bring to the game. Overall, before we can cram such level of abstraction to OSless systems, code organization is the selling point. Secondly, the wall that is holding everyone back, in my view, is the lack of support of medium-big embedded OS for C++. Surely, as they are written in C, you can fiddle around with the compiler using extern and get the OS to work (suggested solution by TI to use their SYS/BIOS TI-RTOS solution). It works, but it is by no way native, and the drivers themselves do not support C++, since they are written in C. Back to Arduino, while it is not the best implementation, peripheral drivers are native in C++, which helps a fuckload. I've written a context switcher/cooperative OS for the Arduino Due just to keep using the C++ features and the Arduino libraries. Last but not least, Thanks for the video! And count me in on those that want to see more.
3:28 - Run both C and C++ versions of blinker on microcontroller, and compare blinking frequency. I think C++ will be 2x slower, because you add port reading and inverting in c++ version.
Hmm, doubt it, but I'll give it a go :) I doubt it because the C functions need to check for nullptr but C++ classes don't have to do that sort of check. Might be a little while before I get around to it but feel free to bring it up again if I forget :)
So doing the same differently, maybe you have very specific needs... as a (newbie) embedded developer I agree sometimes a cookie cuter mentality is the last thing you need. Good content all the same! Hope to see more content like this. I very much like the discussion themes like this can arise.
David Ledger I personally like the struct method. I think people should know what they are doing all the time while programming so it's their job to do things right.
Excellent topic - thank you. Quick question: what's your point of view regarding the memory MCU memory footprint when implementing embedded systems in C++ vs C? This has been a recurring topic of discussion in my career. As an experiment we had a very good C++ developer re-code parts of an embedded real-time system (used in a patient monitor) from C to full-blown C++ where the developer used inheritance, templates and exceptions. The outcome was that we reduced the number of lines of code and improved readability while keeping the memory footprint about the same. At the time this surprised me since I assumed using full-blown C++ introduces code-bloat and I was proven wrong in this one example.
Well yeah this happens to me too I reckon because people do things that are strange: Firstly, people early on compared binary size of cout to printf "Hello, world!" programs, the comparison was deeply flawed as cout does so much more its funny. (I read this online a few times) Secondly, templates have in the past resulted in people instantiating hundreads of a template type which in the distant past duplicated binary, but now with folding in the linker it is becoming a thing of the past. Thirdly, alot people or a while believed that classes all needed a virtual destructor, this simply isn't true and using one results in the instantiation of a VTABLE in the class.
I just got into STM32 programming through my University's Micromouse team, and I'm quite fascinated by the idea of using C++ rather than C in the way you showed, awesome work :D I'd be very interested in a tutorial series perhaps, if you make one :) Keep up the great work
I'll comment without watching the video (but havin skimmed the comments). The embedded gurus at work doesn't mind C++ as long as you keep it out of hard or decently firm real time environments. Basically the gist is that the abstractions make it too hard to keep track of what you are doing in the system and thus you loose determinism, which can be a bit bad if one missed computation slot is critical in order for everything to work safely.
You didn't need to declare another structure in C example. GPIO_Init function after setting values to appropriate register(s) clears structure members.
'You don't have to worry about it' is a problem. Embedded system engineers should worry and do the work. If one's tools are clumsy, sharpen your tool or find a better one; it's dangerous to use a tool that let's you 'get by' without doing the work.
Reading through the comments, we all seem to agree the only benefit of C++ is to deal with C being very verbose. Isn't it time we come up with a new language, a limited "C with classes" which reads easily like java or C# but doesn't go into the usual OO pitfalls and non-sense (factory factories, design patterns, IOC ...). Who's up for the task ?
So, you basically have an abstraction layer more in the cpp version, right? If you would have the same level of abstraction in c then you would end up with a similar number of lines of code. Its always good to have some kind of a "device" abstraction layer between your code and the vendor specific bits.
You can make C modular and apply many aspects of OOP to C (actually you can simulate C++ on C but it's not my point since it would be very extense coding). If you encapsulate your weird looking C statements into exported modules with function names that make sense like "setLedOn()", "setLedOff() and "setLed() // passes an enum with states" you will get a pretty easy to read and use C code
Yes, but you would need to create a 2 full functions for every different GPIO, instead of it being defined by the object type itself. You also need functions like ReadLed and SetLedState, SetLedMode, SetLedPullups and read for all of those to expose the equivalent functionality.
David Ledger yes, your module Led.h would export these functions the same as your class would, instead of coding your methods and state inside a class file, you would code your methods and structs in your Led.h and Led.c, passing around a struct reference for your functions to modify. It's a bit more cumbersome then having all encapsulated and hidden inside a class, but not that much different.
David Ledger my point is just that I don't think you made a fair comparison, if you want to show how encapsulated and maintainable cpp is, then you have to compare it with C, using C good practices, such as modularity, encapsulating state in structs that will be modified by functions that abstract your low level logic.
Don't reinvent the wheel :) XPCC and MODM libraries does this but for an army of MCUs. I would strongly recommend to use them instead rather than writing your own.
That's neat but I have a question. What's the advantage of doing it with C++ and just abstract C a bit more to make it look like what you created in C++? I mean, you can create an abstraction that returns a "pin handler" that would allow you to configure the LED and button, and just pass the port and pin numbers as well as the direction. For example: Pin LED = pinInit(8, 4, OUTPUT); Pin Button = pinInit(8, 11, INPUT); Then that handler would contain in turn an abstraction of the set/reset functions as well as read function so that you could just do something like this: LED.set(Button.read(); And the result would be also 3 lines of code.
Ah neat question. I'm using templates to link with the actual register at compile-time. There is not runtime offset calculation of the pointer in the Pin class. The set/reset functions would be fine but you would also need to implement set and reset for the type of pin, the mode, the speed, the pullups and have a static object manage the power of the GPIO module automatically to make it equivilent. You might also need to implement read functions for all of the above aswell. The advantages: - compile-time calculation of register offsets (so we can actually use the ports registers), - type safety, you cannot physically enter invalid pins or ports, which means *no runtime checks* needed. - Physically less code in the main function - Using standard operators - Full register exposure (all options are available) for all pins without flooding the global namespace with what would be 2 + 2 + 2 + 2 + 2 + 2 (12 functions) functions pin function. - Code is more expressive of programmer intent I believe as it takes less space to understand the same syntax while at the same time using standard grammer
David Ledger ok I see what you are saying, especially with the advantages you mention. May I suggest, you should re-make the video with a head-to-head comparison as also others have suggested. What I mean is, create an abstraction layer similar to the one I suggest in C so that you can compare both styles at the same level, then, you can more clearly and contundently show the C++ advantages over C when showing what you have to do under the hood for both, that is where the comparison will take all its glory.
I started doing that but there is so much pre-requisite knowledge it became really difficult and I gave up and just decided to not show what was under the hood in both cases (STM library or my library). It requires a decent understanding of TMP and I kept getting bogged down in explaining the syntax as it didn't make any sense otherwise.
David Ledger wait, I don't understand, then how did you created yours? Because you need that understanding about the peripheral and boilerplate code that is necessary to be able to know what to put under the hood in order to create an efficient and elegant abstraction. Don't give me wrong, that what you did with C++ is really neat and I've been trying, and maybe others have been also trying, to find a good justification to use C++ on embedded systems. I personally would love to be able to use C++ in MCUs but as many others I think, maybe erroneously, that C++ is overkill or that may not propose a better approach than C. This video shows that may not be the case, but the argument is not too strong without showing under the hood. And yes, I think that it is necessary to defend the C++ case because as you have shown, one could create good and legible code as well as object oriented code which would be very useful for modularization, reuse, and other benefits.
I meant that to do a video about it I would need to teach the audience alot of pre-requisite knowledge before I would be able to explain what was happening. The boilerplate code is linked in through template meta programming which is an advanced subset of C++, something that would be difficult to do a video on without getting completely off topic.
A well written C++ library like this can be compiled into the same size as C. Why does everyone think C++ is horribly inefficient? If you don't use it correctly it can be terribly inefficient. However just like anything worth doing you have to understand the nuances of the language your programming in. Everyone treats C++ like it's C with the extra baggage tacked on. It can be that and when you use it like that it's horribly inefficient. If you truly understand the language, and the nuances of it, you can achieve the efficiency of C. Including things like STL is not something that one should do in a micro controller. String manipulation and link lists are meant for dynamic memory, and not memory constrained micros. Template classes can be a double edge sword, they can reduce run time errors to compile time errors. However if you abstract everything away like the Pin template, how do I know if I have an input or an output? This is where C++ blows up on people. Just because you can overload an operator doesn't necessarily mean it should be done. However C++ does make programming easier, and anything that makes program development easier is something that should not be shamed. Yes it can come at a cost of code size or run time speed. However paying a 1.00 for more memory can be cheaper in some instances than paying someone more money to write in a less abstract language.
Firstly, people early on compared binary size of cout to printf programs, the comparison was deeply flawed as cout does so much more its funny. Secondly, templates have in the past resulted in people instantiating hundreads of a template type which in the distant past duplicated binary, but now with folding in the linker it is becoming a thing of the past. Thirdly, alot people or a while believed that classes all needed a virtual destructor, this simply isn't true and using one results in the instantiation of a VTABLE in the class. These things are trivially avoided, worked around or no longer exist, so I'm 100% with you here.
The example is kind of unfair. The C++ code does give you less control about the exact pin state. I guess you could always change the pin latter as needed, but doing it in an Object Oriented way also tends to add a lot of lines. The C code could be shortened by just writing a function or a macro. (or using reasonable defaults) It is comparing bare C with just controller specific header against C++ with a controller specific libraries.
--- the C++ code does give you less control about the exact pin state It has exactly the same control of pin state, I programmed the library, it has all of the pin settings, but it only exposes what you use. The following is valid working code: LED = State::High; LED = Mode::Input; LED = Speed::High; LED = Resistors::Pullup; LED = Type::OpenDrain; You can also construct the LED object using the above things: Pin LED ( Mode::Input, State::Low, Speed::High, Resistors::Pullup, Type::OpenDrain ); Strictly speaking the C++ code does *more*, as it automatically manages whether the modules power is on or off. Automatically being the operative word here. The automation is scope based too so it doesn't have any performance penalty. It is comparing two libraries essentially, I agree.
And, here is the errors I am getting during compiling: Build started: Project: first steps *** Using Compiler 'V5.06 update 6 (build 750)', folder: 'C:\Keil_v5\ARM\ARMCC\Bin' Build target 'Target 1' compiling test1.cpp... test1.cpp(3): error: #864: Pin is not a template Pin EnterButton; test1.cpp(3): error: #276: name followed by "::" must be a class or namespace name Pin EnterButton; test1.cpp(4): error: #864: Pin is not a template Pin LED (Mode::Output); test1.cpp(4): error: #276: name followed by "::" must be a class or namespace name Pin LED (Mode::Output); test1.cpp(4): error: #276: name followed by "::" must be a class or namespace name Pin LED (Mode::Output); test1.cpp(5): warning: #1-D: last line of file ends without a newline while(true) LED= EnterButton; test1.cpp(5): error: #169: expected a declaration while(true) LED= EnterButton; test1.cpp: 1 warning, 6 errors ".\Objects\first steps.axf" - 6 Error(s), 1 Warning(s). Target not created. Build Time Elapsed: 00:00:01
Looks much better than the stm32 stuff, I wasn't aware of it, thanks. I still prefer my library but its good to have more reference material, and libraries like that make superb reference material.
This is not C++ versus C in my opinion but high level versus low level abstraction libraries . But I like the approach, I don't like the STM32 libraries though.
David, have you tried TrueStudio from Attollic? Its eclipse with lot of features for STM32, and actually STM32 bought company and made it totally free for commercial use with no restrictions. And everything works out of the box in atollic, you don't need to set up anything
That certainly looks a lot easier. I have just been writing the stm32 registers to control the GPIO, it's pretty easy to make mistakes with that. How is the speed and memory usage with your method? I tried some of the examples for the stm32 cube library and it took something like 20KB just to flash a LED.
I'm not that experienced. From what I've read C++ is pretty comparable. But off course it is very compiler dependent. Some can optimize better and check the option. With your example ready made libraries tend to be bloated. So the issue most often is design rather than intrinsic.
The speed is very good, structurally it doesn't need to do anything close to what the STM32 libraries need to do as they have a lot of runtime checks which are required because of pointer issues. Its not superior to all C libraries, but I believe it would be slightly faster than the STM32 library but I haven't analyzed the assembly or run any benchmarks so I dunno yet, I'll report back next uSupply related video. About memory, I don't know I'd have to separate the solution from the rest of the uSupply code but haven't gotten around to it.
Hey Dave2, just a tip, at 8:48 you align the code with tabs, but, if you align with spaces (still indenting with tabs) then anyone can look at the code in their favorite tab-width with everything still in correct alignment. Here's a nice GIF: i.imgur.com/EP5cXem.gifv
Ah, you really should only use tabs for indentation tho, people have different preferences for how wide they want an "indent." And thanks, I made the GIF with HTML + CSS (and its animations). jsfiddle.net/Ashleighz/a6k8m1r7/5/
Why so many thumbs down!? Damn, people are giving the guy a hard time! :) Anyways, with the proper abstraction on the C-side of things, you could've easily ended up with something in the form of: while (true) { set_pin(LED_PIN, get_pin(BUTTON_PIN); }
Firstly, Thankyou! C++ is pretty controversial it seems to C programmers. Yeah your right, that's possible and actually has some advantages. (standard way to expand on pin functionality without modifying the class or struct itself). Its always a trade off though and I like my solution but its not right for everything. The implementation you present would also need the port in the function though and that would result in a runtime calculation of the port registers pointer which is slower than a compile-time calculation (what my library does). Alternatively you could save a pointer to the registers but then the struct that represents the registers is platform specific.
Heh, looks like it is controversial indeed. I wasn't advocating for one over the other, by the way. I think both C and C++ have their place. You're right about the platform specificity, although you could possibly hack some macro magic together in C to accomplish the same thing. It won't be nearly as neat and tidy as a C++ class though :)
There's a byte of manipulation here. Why are you creating 2 separate GPIO init structures? If there was just one you also wouldn't have to set all of its members for each pin. Another thing is that newer ST libraries (HAL and LL) have a toggle function. Also it is possible to write libraries in C that are much easier to use and/or don't require any knowledge of the MCU. What ST has done is a compromise between that, size and overhead.
Have you tried mbed? I haven't managed to get it to work myself (it seems very limited in terms of hardware) but it seems like your C++ example with everything abstracted.
HAHAHA, yeah.... They have made a large push for syntax highlighting in c++17/intellisense like features lately, that is a big deal for me. I tend to burst type and sometimes I swap characters around I use a lot of modern features which just cause all my code to appear as an error in some IDE's even though its completely valid c++17 a whole document of red squiggly lines disturbs me.
Ok. if it has properly working code completion for and highlighting for new c++ that is a valid reason to use it - probably the only valid reason :). To be honest, last time I used eclipse was 5 years ago and it was quite a horrible experience. I'm used to VS - that's a high bar.
hey there, i stumbled across this tut. here is a challenge for you {STM32F4405RGT6 LQFP64 -- USB Device only Custom HID, with 34 generic button clicks external 16mhz OSC Serial Wire Debug for programing only -- pin30(PB11)for LED blink for "im alive" what would be the cleanest way to program that chip so the program took as little space as possible }. just curious, you dont have to do this but i am curious on how small you could make it
That coding style hurts my eyes! Please use space after while([whatever]) and other keywords, not a new line. Like this: while(1) { // infinite loop that will do absolutely nothing } It seems this will be an eternal argument between us programmers...
A lot of embedded code will be in the form of Linux kernel drivers, where C++ is viewed as utterly evil by much of the Linux developer community. I don't expect this to change until Linus dies, and has no say in the matter. This is a huge problem if you want your code to be nice and platform portable. If you KNOW your code will only ever execute on a specific platform, you can use whatever language is most optimal for that platform. Larger projects that have lifetimes of many years often have to migrate to different platforms, and code portability becomes pretty important. I personally think an appropriate C++ subset is very useful for embedded/kernel software development, but it's just not a viable option if you need to be Linux kernel compatible.
You are talking about two completely different things. Your library was designed to be platform agnostic, the ST lib ist not for obvious reasons. The ST way of setting up everything and don‘t making any assumptions is good and safe design. I made a lot of kilometers in out company to help people who made assumptions about init state and failed. I‘m not saying C++ is bad, but in my view pros vs. cons are more of a 50/50 thing.
What a misleading video. Everything you do on the right pane you have to do for your class LED. So to make it look so simple and quick to do in the left pane, is VERY misleading.
I tried this code, and it does not works. Here is the code: #include "stm32f10x.h" Pin EnterButton; Pin LED (Mode::Output); while(true) LED= EnterButton;
Christian Ivarsson You are gravely mistaken www.embedded.com/design/programming-languages-and-tools/4438660/Modern-C--in-embedded-systems---Part-1--Myth-and-Reality
For any embedded programmers, I highly recommend Bob Martin's popular book "Clean Code". It guides you in a similar direction to what Dave is showing here, but goes into detail about why it matters and specific steps on how to get there. Being a EE, I never encountered these ideas in school, so it was very valuable. As others have pointed out, the C code on the right is not really a fair comparison. While functional, the C code still needs to be cleaned. Your main C loop (top level of the application) could look something like this:
if (isButtonPressed())
turnLedOn();
else
turnLedOff();
This is an unfair comparison. Yes, ultimately you can achieve a higher level of abstraction with C++ compared to C but what you are doing there is comparing highly abstracted C++ code with almost non-abstracted C code. It's not so much a language problem but rather a failure to apply the same level of abstraction on both sides of the equation.
Martin Lund So true, without showing the C++ Class/library with all the C++/C/Asm Code behind it's not a fair comparison. Some one have to write it.
pcbreflux Only once, plus C will never give you a compile time error if you set up a port incorrectly, if ports are in conflict etc. I would recommend you to watch the talks by Odin Holmes and have a look at Kvasir MPL and Kvasir Bit library to see the true difference. This is a bit of a contrived example that did not take the full power of TMP into use, as if the initialization code for multiple pins are the same, the register writes can be merged at compile time, giving both flash and speed benefits.
Emil Fresk Sure only once and no doubt more abstraction have clear benefits, but showing only a tiny portion of the code remain a unfair comparison and confuse beginners or "managers".
Indeed, basing anything on one piece of information is not a good strategy. However hopefully it will increase awareness and help the state of the embedded industry today.
I didn't show the code behind the ST library either.
I actually prefer the C way, because I can see exactly what is happening. Sure, I can just put everything in some function in a different file and get the same result, but even for C++ the constructors have to be somewhere in some library. It comes down to how abstract you prefer your main program to be.
Indeed, certainly does :)
How bout a link to your C++ Library ?
Yes, please go into depth on this. Something like 45 minutes explanation of the Pin and Led classes along with the template part would be highly appreciated.
I'm sure this video took some time to produce, but it wasn't a good time spent. It has virtually no sense to compare semi-standard library from a manufacturer in C to a custom library in C++. It's not a comparison between languages, it's a comparison between libraries.
I it surely possible to create a library in C with a similar interface, obviously not using "methods" or overloaded operators, but the same amount of code should fit in around 10 lines of C code.
I thought about this and wasn't sure how it could be done in that manner in an elegant and internally consistent way. The library here exposes all of the functions that the manufacturer library exposes, the only difference is type safety and automatic management of port clock power.
I'd be interested if you had any ideas, I don't like making objects when a function will do but classes are just so powerful.
David, don't know if this would be of interest to you, but how about a lesson on timing constraints of FPGAs, how and why to specify them, impact on propagation, layout, and upper end clock speed, and such? I mention it because it seems a really cool topic, the importance was emphasized in my graduate reconfig computing class but nothing practical was provided.
I would love too, super interesting but I'm not ready for this topic yet.
Its super hard to find good information on that stuff, I've found some RF theory is pretty applicable so been looking into that, not getting anywhere fast.
I love the end card. It's good to see a bit of software stuff on this channel too, modern hardware is half software after all.
I stopped using assembler over 30 years ago. Yet so many people still use it because it is 'fast' or some other arcane reason. C++ which itself is decades old shows how code can be simple, fast, understandable and supportable. The question is will you release your libraries for others to play with? Otherwise we get everyone reinventing the wheel. Yet again.
Yes of course! Its part of the uSupply code, but I need to discuss when to release as much of that project is still secret squirl :) I can release my Pin code I think, i'll check on Monday.
It depends on the situation, about 0.5% of the code I write is assembly language. It's for specific pinch points. While compilers are smart, they don't know anything about your data, so they make broad assumptions. It's not uncommon to be able to double the speed of your code if you can optimise your algorithm to make maximum use of on chip resources like registers rather than going off chip for example. It's rare, but assembly language still has its place.
More common is to be able to read assembly language while debugging, that is a very useful skill.
More fundamentally if your algorithm is not designed for speed, no amount of assembly language will save you!
Yeah that happens, I would recommend checking out compiler directives for particular blocks of code but assembly for certain blocks can do the job too :) Nothing does nop like assembly ;)
Nezbrun -
I've been working with visual studio for years now, and just now (thanks to this video) I realized you can select columns with the cursor by pressing Shift+Alt. Thanks!
just put the C code into functions, it's the exact same thing you did for the C++ example
FWIW, there's really no reason the C code has to be any more complicated (all told) than the C++ code. It's just that you're using a very low-level library of C code and a higher-level library of C++ code. You could build a higher level C library and the code wouldn't look hugely different (you'd have to call a function to set the LED or Enter output, whereas in C++ you presumably use assignment).
> there's really no reason the C code has to be any more complicated
define complicated? easy to read? maintainable? there is, the reason is C, C has few abstractions, C++ has "all the abstractions", RAII alone is a huge productivity, and usability boost, you can't get that with C, because C has no language constructs to control lifetime, the equivalent C code would not just be more verbose but error prone, and there is no way around it
Wow this is awesome. Right now at work we are getting further with our prototypes, and i'm doing this exact transition with our code from the brute force C code, to the much more usable C++ version.
1) You have to mention, that LED is a variable that occupies some space in RAM whereas C code occupies RAM only for the initialization part. It might not be critical for this case, but it might be critical piece of information for somebody, who decides to use your advice; 2) You have put data into the type declaration instead of the instance of the class (struct) yet an instance of your type Pin still occupies some space in RAM even though it holds no state - that is clearly not a good practice from a C++ design standpoint. 3) Since you are encoding state into a type declaration, what do you think will happen, if you define a new variable with the same type? You will get two variables referring to the same pin yet wasting the double amount of RAM to hold both variables; You could have defined a const variable in the global scope, but then your fancy operator overloads would look even stranger; 4) Now lets look at your assignment operator overload - you have a variable that holds no state, instead the assignment operator changes the global state (the GPIO pin); What if you assign one variable of the same Pin type to another? It doesn't do what a typical programmer would except it to do, because the assignment operator doesn't transfer state of the variable but rather copies the global state onto itself. This is mind boggling and is not a good C++ programming practice as well. While it is encouraging that C++ is squeezing into the embedded space (not so long ago the C had a very similar fate trying to compete with assembly language) yet we are not there yet. Making you code not just shorter, but also to obey basic principles of C++ design would require quite a lot of effort. And we are talking only about GPIO pins here. So, maybe there is a reason, why the embedded world is still holding to C and assembly language - these languages are perfectly solving the problem w/o having to dwell into the wonderful world of metaprogramming and building of multi-layer abstractions.
Edit: I just want to add that GPIO pins are very similar animals, but using your technique it is not possible to group them or to put into a collection, which is also something to think about before taking you advice as it is presented.
firstly sizeof(Pin) == 0, you seem to think I'm storing pointers in each Pin class or something...
It is a purely static object (Because physical pins are purely static). It doesn't hold any variables, it connects directly to the registers. My pin operations are equivalent to RAW register operations, only difference is that all the shifts n whatnot are automatic. Depending on compiler flags it can store some global statics, one per unique pin type.
Pins are static and by their nature singleton. It is a compile time error in my configuration to assign one pin using another pin, as I believe it should be. You can assign state of one pin to another etc but is must be explicit or use operators which infer what is needed (boolean operator). Not everything at once, assignments must be explicit and typed. If you wanted a copy of the pin, simply use the type, it is singleton as it is intrinsically linked to a set of registers. It is an approach which avoids global and allows local pin aliases without using references or pointers. The type is a short cut to access individual registers, not all of them at once (except in construction).
If you wanted to assign mode of one pin to another this is valid:
Pin p1;
Pin p2;
p1 = (Mode)p2;
Not all pins have the same capabilities, assignment of a bunch of state that might not be possible is silly and I set out to remove a bunch of runtime checks that other libraries require.
Additionally templates fold in various ways that don't double the binary size, sometimes it increases binary, sometimes it doesn't. This type of behaviour is propagating to many compilers now days its called COMDAT folding in MSVC, gold in gnu icf.
Additionally, template functions are only instantiated when called, unused functions do not enter the binary.
The great thing about C++ is that I have to ask "what operation are you performing when you type 'p1 = (Mode)p2;'?
I mean, you can type that, but I have to go digging around through layers of templates and classes to find out what that code means, because it's not even slightly obvious what you've done there. In C it would be, but in C++ I'm just guessing that by that syntax you're forcing a polymorphic object to surface as a particular class so you can copy just those attributes onto another object?
Under the hood it detects the type you are trying to assign to it and runs the appropriate direct register set/clear to the bit needed. There is no inheritance or the like, and no copies of operator parameters either if I did the forwarding correctly...
Nope, just assign to it with an enum related to pins, the compiler will tell you if its invalid, although all pin enums are valid assignments here.
I would love to see how you built these classes and what's behind that C++ syntax.
To be fair, everything is horrible in Eclipse. :D
Pete Brown That‘s exactly my first thought :)
nothing beats vim.
vi is popular only because no one can figure out how to exit it ;)
I‘m wondering where this meme comes from. Everybody knows its ESC ESC ESC ESC ESC :q!
Just :q
follow up question: try working a button and a LED with the sample GPIO i.e. GPIOA and compile both C & C++ compare and see what actually happens. Along the same case, expand the same situation with more complex cases, there are no doubt C would do a better job.
Ok, so long response. You've opened a complicated can of worms.
Generally, to avoid overhead in C++ IMHO for embedded systems:
1. Avoid heap allocations if possible
2. Don't use virtual functions
3. Use the standard library unless they conflict with the first two laws (many do)
4. Know your compiler flags*
* If your not using exceptions, disable them, and for the love of all things don't compare unoptimized C++ (which is for easy debugging) to code with compiler optimizations. C++ is not designed for production use without the optimizer.
-----------
There are certain things in C++ which can incur a performance penalty, virtual functions, the most widely spread issue IMO. Especially when people started believing all classes need a virtual destructor, this is wrong.
-----------
But there are also things that result in improved performance, these being:
--- constexpr functions to calculate complicate register settings at compile time without resorting to magic numbers or runtime calculations.
--- Some level of type safety without needing to keep runtime type information, which means certain things do not require runtime checks to ensure proper operation.
--- Things like constructors and private members can also provide similar guarantees which enable all member functions to avoid checks which simply aren't possible due to the nature of the object.
--- More effective abstraction allows programmers to look for optimizations instead of being bogged down in micro-optimizations. Large structural issues are easier to see when properly abstracted in my opinion. Micro-optimization is similarly easier when isolated.
-----------
There is also a myth that templates bloat code, they simply do not (well they do if you go nuts), but of course just like inline in C they can be misused.
The mantra of the founders of C++ was "you only pay for what you use".
Some people think that C++ is slower, it isn't, but use it badly (like all things) and it is. It takes quite some experience to learn to use it well.
I maintain a large library (couple hundred thousand lines maybe I don't know) in C++ which is mostly performance focused, I spend large chunks of time benchmarking various algorithms (you can use C code in C++ programs) much of the library is for embedded systems.
I have a lot to learn about this but I am reasonably competent here.
Your explanation is good.
However, in this case you can only reply on the compiler, which in many case are not easily be optimized.
C with ASM always works well in cheap production industry.
As you said, i do agree that working with the "Correct settings" in C++ is an alternative to C.
From heart, how do you expect a rookie would have the same amount of experience.
The C++ code on the left was not platform agnostic since it has "Port::B, 7" in it. On a Raspberry Pi, you just have a pin number and don't worry about ports. On most microcontrollers you'd at least need to think about changing those pin assignments. I think it makes a lot of sense to just have a single pin number like the Raspberry Pi or Arduino environments, so you can put that single number in a macro or constant easily.
Thanks David, I would be interested in more of this type of video.
LOVE IT!! I want more of these video!
You're great David!
Manufacturer libraries for things as simple as GPIO are a waste of time - more stuff to learn, more scope for bugs. Just stupid.
That has been my experience too. I suppose I never really got to that conclusion though, hadn't thought about it, just mindlessly opened manufacturers libraries without thinking like I expect everyone else does.
External library bugs, the absolute worst!
Indeed, For gpio write your own simple bare metal macros or inline functions, the KISS method.
Macros can have their own issues, but yeah inline functions sure :)
Just a couple examples on my issues with macros:
#define cube_volume(x) (x*x*x)
int dimension = 1;
double volume = cube_volume(dimension++);
You'd expect the answer to be 1, but the answer here is 6.
Or:
#define run_two_functions(x) {f1(x); f2(x);}
if (condition)
run_two_functions(x);
else
run_two_functions(y);
The above doesn't work because it evaluates to:
if (condition)
{
};
Of course, like everything you need to know what you're doing, your first example is the classic one they tell you in class, any C programmer worth their salt would be more than aware and wouldn"t really have to think too hard about it.
The second example is unlikely too, you use macros mostly to do simple aliasing and inlining to improve speed at a pinch point, calling functions is possible but unlikely except in the classroom or if you're a macro ninja writing obfuscative code. This is all about improving readability and maintainability, not making it worse!
I can't think of a time I've ever fallen with your examples jn practice in 30 years of C programming. Even inlining has its challenges, there is no guarantee it will inline your code for example, and debugging inline functions often doesn't work well. My problem with templates is that the concept came in late in my career, so it's not something I'd naturally use. I also find the syntax of them far from natural, like it was bolted onto the language, which it was of course!
Writing to a port for the most part shouldn't need the compiler to generate a function call at all in an embedded system that doesn't have an OS, it should generate code to do direct peripheral register access.
David, I don't think this is about C vs C++. One one side, you are using a nice GPIO framework, and on the other side, you are using STM's GPIO library.
You demonstrated that STM's library is horrible, but this has nothing to do with C vs C++. You can have a sane GPIO framework in C, and you wouldn't have to worry about "all this". Or you can have a bad framework in C++.
You have demonstrated that a good library will simplify the code and improve readability. I am in full agreement with this conclusion. But a good library has little to do with the language.
Some languages lend themselves to better abstraction, but yeah no doubt the STM library could have been better.
Was a real world example, for a project we are working on, the STM library was available, and I wrote my library to avoid its verbosity.
Interesting video, thanks! I would enjoy an in depth video discussing this topic further.
While the 'unfair comparison' argument does have some merit, I can see your point. The problem I have is.. embedded. A C++ object library designed for one microcontroller not only isn't going to work for another micro, but it will be a PITA to convert. Even in the STM32 line, the pinouts and capabilities differ widely. Any good code should be well commented, and that goes double for code that will be seen by others, _triple_ for code you're going to make public domain. If your regular C example is similar to the C++ object definitions, they're not commented at all! If I were give the task to convert that code for a different microcontroller, I wouldn't. I'd study it enough to get the gist of the process and what variables and functions needed to be exposed, and write my own.
Personally I would much rather use rust than either of C or C++. Thankfully there is some work going on to make it more useable on microcontrollers and the like.
Couldn't agree more. People should definitely checkout Jorge Aparicios work on Embedded Rust at blog.japaric.io/
Rust is the duck's guts. Let's hope it becomes more mainstream.
Yeah, I'm awaiting a few developments before I port my libraries but looks promising.
I've been involved in embedded development for over 18 years and have steadily been moving up the stack over that time. From assembly, to C to C++ and from bare metal to rtos. And I'm never going back. Look forward in the future to moving even further up to stack and being able to get more done in less time.
did anyone notice the "uSupply" ??!! excited
you can get around with overloading operator= instead of the Pin::Get and Pin::Set functions; having the pin port and number as template arguments, honestly feels like abusing the type system, I think they should be normal constructor arguments, it also going to make it hard to store Pin objects in a contrainer, as each Pin is a different type if their template arguments are different
Please please please go in depth, you have no idea how great a long video about this would be!!!!
Then again you could put everything in a function and make it as simple to the C++ example. Which you are basicly doing in C++.
No. You cannot assign to function results in C, one function cannot contain all functionality of the operator enabled class.
The advantages of the class:
- compile-time calculation of register offsets (so we can actually use the ports registers),
- type safety, you cannot physically enter invalid pins or ports, which means *no runtime checks* needed.
- Physically less code in the main function
- Using standard operators
- Full register exposure (all options are available) for all pins without flooding the global namespace with what would be 2 + 2 + 2 + 2 + 2 + 2 (12 functions or 6 if you want to sacrifice some speed) functions pin function.
- Code is more expressive of programmer intent I believe as it takes less space to understand the same syntax while at the same time using standard grammer
One function also requires runtime calculation of port offsets for the port registers or alternatively you need to maintain a platform specific pointer in your main application code but that is hardly portable as the platform specific structure isn't portable (similar to the ST library).
Remember the class here exposes every function of the port, automatically manages the port clock, and provides type safety which prevents the need for any runtime checks (nullptr, invalid port index, invalid pin number etc).
If theres a neat way to do it post it here, I'm always interested :)
I would love to see the speed comparison of both the codes as well as the total bytes consumed. When you are doing very advance level stuffs then I would imagine C++ would complicate the things. Thats why C was prefered choice for building operating system codes.
The total bytes should be the same or less. All the template stuff is just resolved at compile time. It's like #define in a type safety manner. And if you create advanced level stuff, C++ will help you to structure the code.
Especially if you need to manage resources. For example, a lock class automatically releases the lock when the instance goes out of scope (you leave the function/method/case/loop-iteration where the lock was created).
Also compile time functions can generate lookup tables without any runtime.
And C is only preferred because all operating systems are old. Before 2011, C++ had problems solving certain problems properly. Since then much stuff has been changed.
Look for C++ and embedded programming on youtube. You will find some lectures on the topic. As an example of modern C++, also a pong game for a C64 has been used. You can not do things in C as easy as in C++.
@@kla_sch3864 C++ is not intended for performance but yes OOP makes thing easier to manage. That's why C is not for all. in C++ memory footprint is much larger even for doing small things rest aside the speed performance.
Cheers, David! I liked your explanation a lot. Ivan
I would like more of this kind of stuff, I really enjoyed this video. No-nonsense, just a tutorial
They not "setting up a clock", they're enabling the clocking of the peripheral which is STMs way of power gating function blocks. It's actually important that this is verbose, I'd even recommend you check what your C++ code does because if it happens to be stupid enough to enable all ports even if you're just using a single pin, you're wasting (albeit a tiny bit of) power. Things like that also become very important if you want to use sleep modes. It might seem tedious and superfluous to a beginner but one you reach the more advanced levels of MCU programming having the control over tidbits like becomes an important affair. NB: I haven't looked at the C++ abstractions, maybe they offer fine grain control or not but at least it's good to know about this stuff.
Firstly, I like you, you clearly know what your talking about! Thanks for the comment, very relevant. But....
Nope you haven't given me enough credit here. It automatically detects what port is used (at compile-time) and enables the minimal number of ports needed. It also only does this once per port used, it doesn't even check a second time the port is used. It also can disable ports once no pins on that port are in use (out of scope).
Dave, your cold sounds awful. Take a few days off man!
Its not him
@@gamerm4822 nil faeces Mr Holmes
The real differences were not covered in this video, sadly. Speed and Memory Overhead.
Speed should be identical, abtractions in C++ don't incur penalty, memory potentially could be but I believe my template code folds so it doesn't add to memory overhead.
The C++ optimizer also wasn't covered, and this is the really cool part. The optimizer is the thing which goes around doing things like removing unneeded variables, inlining functions, removing entire objects and functions if they turn out to be unneeded. As you should know from the C optimizer, what you write isn't necessarily what you get when compiled. The C optimizer can do things like constant folding, remove unneeded variables and so on.
The result of this is that quite often the code generated for C++ is comparable to the code generated for C. And this is common for PC applications, so why would there be any difference for embedded?
Might be a bit late, but are you able to post the assembly code of your LED = State::High vs the C function call to set the output high? I'd be interested to see whether it actually does fold down as far as you expect or if the compiler does something else :)
Dave, I'm really trying to be open minded here but I'm not sure how long lasting it is to use c++ on limited micros.
I have a design I'm working on using the stm32f042k6u6, great chip,32KB of flash, lots of peripherals; But once I took STs MXCube code generator and It generated all the init code I needed, to my surprise just the init code took over 60% of the chips flash!
Referencing an article from the embedded muse about how these APIs are usually written (Junior devs that write and then god know who maintains the code), it seems the best way was to rewrite the code on you own.
going in deep you can see that the programmer that implemented the API wrote a lot of safety code that creates a lot of overhead:
1. passing large configuration structs around
2. asserts on parameters
3. spin locks for multi task codes
Just a lot of code that is best implemented by good writing by the customer rather than the vendor.
To make a long story short, My entire code including the init functions, working with an oled lcd, spi flash interface, i2c, USB,RTC, Timers, Interrupts and ADC only takes about 50% of the flash.
While the my code is non generic (my functions assume single interfaces that you supply a pointer to externally, just to save code size.) it works great and no one died yet.
Going into c++ might contain the same overhead that will quickly guzzle up the flash I barely have, So while blinking leds and reading buttons is nice, I'd stay clear from using a higher language for flash limited devices.
Heck if it was an f4 or an f7 where you have a substantial amount of flash, I say go crazy with it, use C++; But for tiny Embedded Systems, I'm sticking with C.
It really is a function of the API, C++ doesn't intrinsically consume more memory but developers sometimes get carried away and can do things in their code which result in large code bloat. exceptions and virtual functions are two major things that really take memory usage but you by no means need to use them.
If C works for you, thats fine :) I'm just showing what I do.
It would be great if you can upload the code to github, maybe try it a bit...
C++ can easily provide zero-cost abstractions, there is absolutely nothing inherent in the language that causes code bloat. If a project has code bloat, it's because of the code, not the language.
And with ASM web services can be easily created, there is absolutely nothing inherent in the language that causes code grown to overcomplicated piles of unreadable data.
JAVA can easily provide zero-cost abstractions, there is absolutely nothing inherent in the language that causes code bloat. If a project has code bloat, it's because of the code, not the language.
You can hear the dinosaurs roaring: "All we need is C. C++ is slow."
All versions of C are dinosaurs. Extreme terseness, low human readability, no data bounds checking and case sensitivity, all to minimize processor time on the puny 8-bit micros of the 1970's.
Even if compiling code for running on a tiny processor it makes no sense to have a PC interface which behaves as if it was running on same. Why not use a more human readable syntax, with meaningful keywords instead of bracketbracketbracketbracketbracketbracketbracketbracketbracketbracketbracket, case insensitivity, and bounds checking so as to avoid security vulns arising?
Something like 80% of all IT vulns are down to C/C++ buffer overflows.
If 80% of deaths in road accidents were due to not wearing seatblets they'd make it mandatory to wear them.
-Wait a minute, they have.
"all to minimize processor time on the puny 8-bit micros of the 1970's."
BULLSHIT. C was designed for making Unix, and it does what it's designed to do quite well.
C is the language of choice for writing operating system kernels. (With a sprinkle of assembly for interrupt management and such.)
Nice touch! Let's continue! Using pure C or C++ have sense in lot of cases, because programming MCU is not so trivial like standard programming. I'm using both because it's dependent on performance and control of your code. But off course you have to be professional to write good code in pure C.
Thx for your comparison. Eventually I would agree that C++ has nice advantages. BUT I think you but C into a unnecessarily bad light, as it's not a big deal to get the exact same two-liner in C as you have in C++. You can easily hide the definitions you show for the C example into a further abstraction, as it is done in your C++ example already.
Would love to see the next videos. Keep up the good work.
Really good job,programming is not really my thing but you have a really amazing way of explaining things,Thanks!
Not all uC manufacturers have free C++ compilers available, it's probably relevant to the Cortex-M series but other micro's I prefer to write to the registers direct, be it in C or assembler - like PIC12 for example. All this video really shows is something you've written to abstract, you should have shown something in C and then the same thing in C++ without any custom layers.
True, even PIC18 barely has any options.
This was made a a real example, real code, real device, real provided library (with slight modification for this video).
I am a working engineer, its hard to justify making the same thing twice with the foreknowledge that half of the work is entirely redundant.
So, you could have just as easily created the same "library" with only 3 / 4 lines of code in C, just as well as you did with C++, looking at the library code, it's no different from the C counterpart, you just chose to show the library calls for C++ and the GPIO calls for the C example, so in reality, not a fair comparison?
Yes, but somewhere you had to code the classes. You don't get the class (and its code) for free. So, if you made a mistake when coding the class you wind up with the same issue as making a mistake in the C code. In fact, there are no real savings here in coding in C++ vs C.
He is using the generated code in the C example and he had to do that for the class in the C++ example (or write his own register and bit access code). I could just as easily create a function in C that handled all the details like a class. Then I could make it as pretty as the C++ example. So no savings in the amount of code, and no savings in what I have to remember when setting up pins.
Realistically, if all you are doing is blinking an LED and some other simple tasks, then the STM32 processor and the HAL are way overkill. You should just use an 8-bit microcontroller. Or the LL drivers for the STM32, or to really simplify it just code to the registers and set up your own functions to access the registers instead of the excessive layers of the HAL/LL.
What is being shown in the C++ example is very misleading to new embedded engineers. There is a ton of code in the class doing the same thing as the C code but it is hidden, which you can do in C.
I am not against using C++ in bare-metal embedded firmware, and it can be just as small and just as fast (I did the testing and found a 4-byte and 2-instruction cycle difference between the C++ and C code due to the constructor call which is 1 address added to the stack and 1 branch). Yes, I like that you can hide the details in C++, but what I have a problem with is in this example the details could have been as easily hidden in C.
This is a very misleading way to say C++ is better than C. They can produce the same code and both can look as simple as the C++ example. Coding the class is just as difficult as the code he shows for the C example. I say show us the class code.
Bit biased?
Just adding myself to the list of people interested in a multi hour video.
Hey David, I don't fully agree with the examples that you've given, for that well, managing pins has always been a problem for me.
Sure, STM32s and ATMELs, if I'm not mistaken, you can control the pin directly on peripheral. On some TI chips you need to configure the pin pad, and then the gpio peripheral, if not some other control structure crap.
Now, I do agree on the use of C++ on other topics, such as the numpad (yes, to use that you need some sort of pin abstraction). That is somewhat Arduino is doing (after some articles online, I've concluded that they are/were doing it badly).
I think that the strongest point that C++ has to offer to the embeded community is not the abstraction layers, at least not in the first features.
Namespaces, structs/classes, inheritance and as mentioned by others here in the comments, the optimization that the compilers bring to the game.
Overall, before we can cram such level of abstraction to OSless systems, code organization is the selling point.
Secondly, the wall that is holding everyone back, in my view, is the lack of support of medium-big embedded OS for C++. Surely, as they are written in C, you can fiddle around with the compiler using extern and get the OS to work (suggested solution by TI to use their SYS/BIOS TI-RTOS solution).
It works, but it is by no way native, and the drivers themselves do not support C++, since they are written in C.
Back to Arduino, while it is not the best implementation, peripheral drivers are native in C++, which helps a fuckload. I've written a context switcher/cooperative OS for the Arduino Due just to keep using the C++ features and the Arduino libraries.
Last but not least, Thanks for the video!
And count me in on those that want to see more.
3:28 - Run both C and C++ versions of blinker on microcontroller, and compare blinking frequency. I think C++ will be 2x slower, because you add port reading and inverting in c++ version.
Hmm, doubt it, but I'll give it a go :) I doubt it because the C functions need to check for nullptr but C++ classes don't have to do that sort of check.
Might be a little while before I get around to it but feel free to bring it up again if I forget :)
So doing the same differently, maybe you have very specific needs... as a (newbie) embedded developer I agree sometimes a cookie cuter mentality is the last thing you need. Good content all the same! Hope to see more content like this. I very much like the discussion themes like this can arise.
To be clear, the pin class exposes all of the functionality of the other library, it just does it with defaults, operator overloading and type safety.
David Ledger I personally like the struct method. I think people should know what they are doing all the time while programming so it's their job to do things right.
Excellent topic - thank you. Quick question: what's your point of view regarding the memory MCU memory footprint when implementing embedded systems in C++ vs C?
This has been a recurring topic of discussion in my career. As an experiment we had a very good C++ developer re-code parts of an embedded real-time system (used in a patient monitor) from C to full-blown C++ where the developer used inheritance, templates and exceptions. The outcome was that we reduced the number of lines of code and improved readability while keeping the memory footprint about the same. At the time this surprised me since I assumed using full-blown C++ introduces code-bloat and I was proven wrong in this one example.
Well yeah this happens to me too I reckon because people do things that are strange:
Firstly, people early on compared binary size of cout to printf "Hello, world!" programs, the comparison was deeply flawed as cout does so much more its funny. (I read this online a few times)
Secondly, templates have in the past resulted in people instantiating hundreads of a template type which in the distant past duplicated binary, but now with folding in the linker it is becoming a thing of the past.
Thirdly, alot people or a while believed that classes all needed a virtual destructor, this simply isn't true and using one results in the instantiation of a VTABLE in the class.
Unfortunately, I cannot find your interface in the public domain. I think I might like your interface, but I cannot use what I do not have.
Interesting this is my first look at embedded code.
I just got into STM32 programming through my University's Micromouse team, and I'm quite fascinated by the idea of using C++ rather than C in the way you showed, awesome work :D
I'd be very interested in a tutorial series perhaps, if you make one :)
Keep up the great work
I'll comment without watching the video (but havin skimmed the comments). The embedded gurus at work doesn't mind C++ as long as you keep it out of hard or decently firm real time environments. Basically the gist is that the abstractions make it too hard to keep track of what you are doing in the system and thus you loose determinism, which can be a bit bad if one missed computation slot is critical in order for everything to work safely.
You didn't need to declare another structure in C example. GPIO_Init function after setting values to appropriate register(s) clears structure members.
'You don't have to worry about it' is a problem. Embedded system engineers should worry and do the work. If one's tools are clumsy, sharpen your tool or find a better one; it's dangerous to use a tool that let's you 'get by' without doing the work.
Reading through the comments, we all seem to agree the only benefit of C++ is to deal with C being very verbose. Isn't it time we come up with a new language, a limited "C with classes" which reads easily like java or C# but doesn't go into the usual OO pitfalls and non-sense (factory factories, design patterns, IOC ...). Who's up for the task ?
just use openCM3 instead of HAL. It decrease C code in a half.
So, you basically have an abstraction layer more in the cpp version, right? If you would have the same level of abstraction in c then you would end up with a similar number of lines of code.
Its always good to have some kind of a "device" abstraction layer between your code and the vendor specific bits.
You can make C modular and apply many aspects of OOP to C (actually you can simulate C++ on C but it's not my point since it would be very extense coding). If you encapsulate your weird looking C statements into exported modules with function names that make sense like "setLedOn()", "setLedOff() and "setLed() // passes an enum with states" you will get a pretty easy to read and use C code
That being said, I just watched the start of the video, don't know if you talk about it later, so I thought it would be nice to share anyway.
Yes, but you would need to create a 2 full functions for every different GPIO, instead of it being defined by the object type itself. You also need functions like ReadLed and SetLedState, SetLedMode, SetLedPullups and read for all of those to expose the equivalent functionality.
David Ledger yes, your module Led.h would export these functions the same as your class would, instead of coding your methods and state inside a class file, you would code your methods and structs in your Led.h and Led.c, passing around a struct reference for your functions to modify. It's a bit more cumbersome then having all encapsulated and hidden inside a class, but not that much different.
David Ledger my point is just that I don't think you made a fair comparison, if you want to show how encapsulated and maintainable cpp is, then you have to compare it with C, using C good practices, such as modularity, encapsulating state in structs that will be modified by functions that abstract your low level logic.
You should include the library with the class into the comparison. This is like comparing apples vs oranges. Or write a C library.
That head swap at the end of the video is great =))
Need efficient way of setting pin? No problem. *((volatile uint32_t*)0x40020418)=0x00000002;
Don't reinvent the wheel :) XPCC and MODM libraries does this but for an army of MCUs. I would strongly recommend to use them instead rather than writing your own.
MODM has very different goals to my libraries, but it is some really great reference material for when goals overlap :D
That's neat but I have a question.
What's the advantage of doing it with C++ and just abstract C a bit more to make it look like what you created in C++?
I mean, you can create an abstraction that returns a "pin handler" that would allow you to configure the LED and button, and just pass the port and pin numbers as well as the direction. For example:
Pin LED = pinInit(8, 4, OUTPUT);
Pin Button = pinInit(8, 11, INPUT);
Then that handler would contain in turn an abstraction of the set/reset functions as well as read function so that you could just do something like this:
LED.set(Button.read();
And the result would be also 3 lines of code.
Ah neat question.
I'm using templates to link with the actual register at compile-time. There is not runtime offset calculation of the pointer in the Pin class.
The set/reset functions would be fine but you would also need to implement set and reset for the type of pin, the mode, the speed, the pullups and have a static object manage the power of the GPIO module automatically to make it equivilent. You might also need to implement read functions for all of the above aswell.
The advantages:
- compile-time calculation of register offsets (so we can actually use the ports registers),
- type safety, you cannot physically enter invalid pins or ports, which means *no runtime checks* needed.
- Physically less code in the main function
- Using standard operators
- Full register exposure (all options are available) for all pins without flooding the global namespace with what would be 2 + 2 + 2 + 2 + 2 + 2 (12 functions) functions pin function.
- Code is more expressive of programmer intent I believe as it takes less space to understand the same syntax while at the same time using standard grammer
David Ledger ok I see what you are saying, especially with the advantages you mention. May I suggest, you should re-make the video with a head-to-head comparison as also others have suggested.
What I mean is, create an abstraction layer similar to the one I suggest in C so that you can compare both styles at the same level, then, you can more clearly and contundently show the C++ advantages over C when showing what you have to do under the hood for both, that is where the comparison will take all its glory.
I started doing that but there is so much pre-requisite knowledge it became really difficult and I gave up and just decided to not show what was under the hood in both cases (STM library or my library).
It requires a decent understanding of TMP and I kept getting bogged down in explaining the syntax as it didn't make any sense otherwise.
David Ledger wait, I don't understand, then how did you created yours? Because you need that understanding about the peripheral and boilerplate code that is necessary to be able to know what to put under the hood in order to create an efficient and elegant abstraction.
Don't give me wrong, that what you did with C++ is really neat and I've been trying, and maybe others have been also trying, to find a good justification to use C++ on embedded systems.
I personally would love to be able to use C++ in MCUs but as many others I think, maybe erroneously, that C++ is overkill or that may not propose a better approach than C. This video shows that may not be the case, but the argument is not too strong without showing under the hood.
And yes, I think that it is necessary to defend the C++ case because as you have shown, one could create good and legible code as well as object oriented code which would be very useful for modularization, reuse, and other benefits.
I meant that to do a video about it I would need to teach the audience alot of pre-requisite knowledge before I would be able to explain what was happening.
The boilerplate code is linked in through template meta programming which is an advanced subset of C++, something that would be difficult to do a video on without getting completely off topic.
A well written C++ library like this can be compiled into the same size as C. Why does everyone think C++ is horribly inefficient? If you don't use it correctly it can be terribly inefficient. However just like anything worth doing you have to understand the nuances of the language your programming in. Everyone treats C++ like it's C with the extra baggage tacked on. It can be that and when you use it like that it's horribly inefficient. If you truly understand the language, and the nuances of it, you can achieve the efficiency of C. Including things like STL is not something that one should do in a micro controller. String manipulation and link lists are meant for dynamic memory, and not memory constrained micros. Template classes can be a double edge sword, they can reduce run time errors to compile time errors. However if you abstract everything away like the Pin template, how do I know if I have an input or an output? This is where C++ blows up on people. Just because you can overload an operator doesn't necessarily mean it should be done. However C++ does make programming easier, and anything that makes program development easier is something that should not be shamed. Yes it can come at a cost of code size or run time speed. However paying a 1.00 for more memory can be cheaper in some instances than paying someone more money to write in a less abstract language.
Firstly, people early on compared binary size of cout to printf programs, the comparison was deeply flawed as cout does so much more its funny.
Secondly, templates have in the past resulted in people instantiating hundreads of a template type which in the distant past duplicated binary, but now with folding in the linker it is becoming a thing of the past.
Thirdly, alot people or a while believed that classes all needed a virtual destructor, this simply isn't true and using one results in the instantiation of a VTABLE in the class.
These things are trivially avoided, worked around or no longer exist, so I'm 100% with you here.
The example is kind of unfair. The C++ code does give you less control about the exact pin state. I guess you could always change the pin latter as needed, but doing it in an Object Oriented way also tends to add a lot of lines. The C code could be shortened by just writing a function or a macro. (or using reasonable defaults) It is comparing bare C with just controller specific header against C++ with a controller specific libraries.
--- the C++ code does give you less control about the exact pin state
It has exactly the same control of pin state, I programmed the library, it has all of the pin settings, but it only exposes what you use.
The following is valid working code:
LED = State::High;
LED = Mode::Input;
LED = Speed::High;
LED = Resistors::Pullup;
LED = Type::OpenDrain;
You can also construct the LED object using the above things:
Pin LED
(
Mode::Input,
State::Low,
Speed::High,
Resistors::Pullup,
Type::OpenDrain
);
Strictly speaking the C++ code does *more*, as it automatically manages whether the modules power is on or off. Automatically being the operative word here. The automation is scope based too so it doesn't have any performance penalty.
It is comparing two libraries essentially, I agree.
no way, all bet on C
Hey David, get working on them multi hour tutorial videos!
And, here is the errors I am getting during compiling:
Build started: Project: first steps
*** Using Compiler 'V5.06 update 6 (build 750)', folder: 'C:\Keil_v5\ARM\ARMCC\Bin'
Build target 'Target 1'
compiling test1.cpp...
test1.cpp(3): error: #864: Pin is not a template
Pin EnterButton;
test1.cpp(3): error: #276: name followed by "::" must be a class or namespace name
Pin EnterButton;
test1.cpp(4): error: #864: Pin is not a template
Pin LED (Mode::Output);
test1.cpp(4): error: #276: name followed by "::" must be a class or namespace name
Pin LED (Mode::Output);
test1.cpp(4): error: #276: name followed by "::" must be a class or namespace name
Pin LED (Mode::Output);
test1.cpp(5): warning: #1-D: last line of file ends without a newline
while(true) LED= EnterButton;
test1.cpp(5): error: #169: expected a declaration
while(true) LED= EnterButton;
test1.cpp: 1 warning, 6 errors
".\Objects\first steps.axf" - 6 Error(s), 1 Warning(s).
Target not created.
Build Time Elapsed: 00:00:01
You can use libopencm3 and still have C without typical stm32 hal verbosity.
Looks much better than the stm32 stuff, I wasn't aware of it, thanks. I still prefer my library but its good to have more reference material, and libraries like that make superb reference material.
This is not C++ versus C in my opinion but high level versus low level abstraction libraries . But I like the approach, I don't like the STM32 libraries though.
Great video!
Whatever works within given resources, is good
Yes.
aren't programmers paid by how many lines they write? ;)
I dont get it. Why you dont have to set up things like GPIO_Speed with the cpp version???
David, have you tried TrueStudio from Attollic? Its eclipse with lot of features for STM32, and actually STM32 bought company and made it totally free for commercial use with no restrictions. And everything works out of the box in atollic, you don't need to set up anything
I haven't tried it, thakns.!
That certainly looks a lot easier. I have just been writing the stm32 registers to control the GPIO, it's pretty easy to make mistakes with that.
How is the speed and memory usage with your method?
I tried some of the examples for the stm32 cube library and it took something like 20KB just to flash a LED.
I'm not that experienced. From what I've read C++ is pretty comparable. But off course it is very compiler dependent. Some can optimize better and check the option. With your example ready made libraries tend to be bloated. So the issue most often is design rather than intrinsic.
The speed is very good, structurally it doesn't need to do anything close to what the STM32 libraries need to do as they have a lot of runtime checks which are required because of pointer issues.
Its not superior to all C libraries, but I believe it would be slightly faster than the STM32 library but I haven't analyzed the assembly or run any benchmarks so I dunno yet, I'll report back next uSupply related video.
About memory, I don't know I'd have to separate the solution from the rest of the uSupply code but haven't gotten around to it.
20K to flash an LED is a disgrace. Modern programmers just love their abstraction and layer upon layer of unneccesary crap.
That's why I will never use the stm cube crap.
They try to support every microcontroller with one library and it's just a bloated mess.
Probably only half of the story. Where is the part disabling exceptions and declaring methods as noexcept and comparing elf sizes?
Hey Dave2, just a tip, at 8:48 you align the code with tabs, but, if you align with spaces (still indenting with tabs) then anyone can look at the code in their favorite tab-width with everything still in correct alignment. Here's a nice GIF: i.imgur.com/EP5cXem.gifv
Haha yeah thanks. its because the st library indented differently to me (spaces only).
How did you make that GIF its awesome!
Ah, you really should only use tabs for indentation tho, people have different preferences for how wide they want an "indent." And thanks, I made the GIF with HTML + CSS (and its animations).
jsfiddle.net/Ashleighz/a6k8m1r7/5/
I use tabs, ST library use spaces only, my last comment was a bit confusing there. Anyway thanks :)
Ah, phew lol...
Why so many thumbs down!? Damn, people are giving the guy a hard time! :)
Anyways, with the proper abstraction on the C-side of things, you could've easily ended up with something in the form of:
while (true) { set_pin(LED_PIN, get_pin(BUTTON_PIN); }
Firstly, Thankyou! C++ is pretty controversial it seems to C programmers.
Yeah your right, that's possible and actually has some advantages.
(standard way to expand on pin functionality without modifying the class or struct itself).
Its always a trade off though and I like my solution but its not right for everything.
The implementation you present would also need the port in the function though and that would result in a runtime calculation of the port registers pointer which is slower than a compile-time calculation (what my library does).
Alternatively you could save a pointer to the registers but then the struct that represents the registers is platform specific.
Heh, looks like it is controversial indeed. I wasn't advocating for one over the other, by the way. I think both C and C++ have their place. You're right about the platform specificity, although you could possibly hack some macro magic together in C to accomplish the same thing. It won't be nearly as neat and tidy as a C++ class though :)
please go in depth that would be amazing
There's a byte of manipulation here. Why are you creating 2 separate GPIO init structures? If there was just one you also wouldn't have to set all of its members for each pin. Another thing is that newer ST libraries (HAL and LL) have a toggle function. Also it is possible to write libraries in C that are much easier to use and/or don't require any knowledge of the MCU. What ST has done is a compromise between that, size and overhead.
Thats true, could have changed the minimal things I didn't think about it forgot that the structures are not needed after the function call.
Have you tried mbed? I haven't managed to get it to work myself (it seems very limited in terms of hardware) but it seems like your C++ example with everything abstracted.
Don't be so hard on yourself, don't inflict eclipse on yourself. No body deserves that. :)
HAHAHA, yeah.... They have made a large push for syntax highlighting in c++17/intellisense like features lately, that is a big deal for me. I tend to burst type and sometimes I swap characters around I use a lot of modern features which just cause all my code to appear as an error in some IDE's even though its completely valid c++17 a whole document of red squiggly lines disturbs me.
Ok. if it has properly working code completion for and highlighting for new c++ that is a valid reason to use it - probably the only valid reason :). To be honest, last time I used eclipse was 5 years ago and it was quite a horrible experience. I'm used to VS - that's a high bar.
hey there, i stumbled across this tut. here is a challenge for you {STM32F4405RGT6 LQFP64 -- USB Device only Custom HID, with 34 generic button clicks external 16mhz OSC Serial Wire Debug for programing only -- pin30(PB11)for LED blink for "im alive" what would be the cleanest way to program that chip so the program took as little space as possible }. just curious, you dont have to do this but i am curious on how small you could make it
That coding style hurts my eyes! Please use space after while([whatever]) and other keywords, not a new line. Like this:
while(1) {
// infinite loop that will do absolutely nothing
}
It seems this will be an eternal argument between us programmers...
A lot of embedded code will be in the form of Linux kernel drivers, where C++ is viewed as utterly evil by much of the Linux developer community. I don't expect this to change until Linus dies, and has no say in the matter. This is a huge problem if you want your code to be nice and platform portable. If you KNOW your code will only ever execute on a specific platform, you can use whatever language is most optimal for that platform. Larger projects that have lifetimes of many years often have to migrate to different platforms, and code portability becomes pretty important. I personally think an appropriate C++ subset is very useful for embedded/kernel software development, but it's just not a viable option if you need to be Linux kernel compatible.
I don't understand the dislikes, this was one of the best videos from this channel.
Some people have turned their way of doing things into a religion, and anything that provokes thought or discussion about it is, to them, bad.
You are talking about two completely different things. Your library was designed to be platform agnostic, the ST lib ist not for obvious reasons. The ST way of setting up everything and don‘t making any assumptions is good and safe design. I made a lot of kilometers in out company to help people who made assumptions about init state and failed. I‘m not saying C++ is bad, but in my view pros vs. cons are more of a 50/50 thing.
font size pls
Why is there an automatic translation of subtitles in, for example, Ukrainian and Polish is not? Greetings from Poland.
If you don't like low level control then use APL!
What a misleading video. Everything you do on the right pane you have to do for your class LED. So to make it look so simple and quick to do in the left pane, is VERY misleading.
nothing beats efficiency of ansi-c low level programming
You need to study the opensource C code used in Quake 3 and DOOM3. You wont look at C the same way ever again.
I tried this code, and it does not works. Here is the code:
#include "stm32f10x.h"
Pin EnterButton;
Pin LED (Mode::Output);
while(true) LED= EnterButton;
Ugh. The trainwreck that is C++... Fine on a computer but I'd never use it on small, embedded platforms. As you know it's a resource hog.
Christian Ivarsson You are gravely mistaken www.embedded.com/design/programming-languages-and-tools/4438660/Modern-C--in-embedded-systems---Part-1--Myth-and-Reality
I wish you had bigger fonts. small fonts really suck.
Nice closing picture.
I expected dave1's voice....then I had to check if I watch the correct channel.....