Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Awesome guide on how to get the best out of C++ on embedded systems.

One thing I would add is: enable all the warnings you can and make them errors: "-Wall -Wextra -Wpedantic -Werror".

I know it's hard to do in practice (who will want to fix the thousands of warnings in a well seasoned codebase?) but it's a good idea for new projects. Especially if team uses the same compiler (sometimes the code produces warning with newer gcc versions).



Yes! This was a practice beat into me (not literally, but close) at my first programming gig in the 1980s. "A compile should not produce warnings! If the code producing the warning is truly intentional & deliberate, then grab a senior developer to review and ONLY if he agrees we'll issue a #pragma directive to tell the compiler to ignore it. But most of the time, (99%+) there's a way to do what you want without triggering a warning."

Most of the time it was simple mistakes or laziness like a downcast conversion leading to loss of precision, etc. but occasionally on review we'd find somethign really stupid/dangerous like type mismatches on a pointer ("well, no, that index into a string should NOT be used to reference memory...") or a return before a free() call (memory leak), uninitialized values (yay for random behavior!).

I thought sloppy, warning-riddled code was ancient history until last week, when I did a fresh install of R Studio on a server + a couple dozen packages, watching page after page of gcc compiler warnings scroll past.


> I thought sloppy, warning-riddled code was ancient history until last week, when I did a fresh install of R Studio on a server + a couple dozen packages, watching page after page of gcc compiler warnings scroll past.

Nah, just compile or use any Gtk+ based application.


Oh yes, that is true.

I launch every program from terminals and a terrible warning parade fills my terminals whenever it is a Gtk application. I came to think that the developers never actually launch their program this way and never the see the flow of warnings they trigger.


I might misunderstand what kind of warnings you are talking about, but I saw OP talking about compiler warnings during the build process and you about GTK application runtime warnings. They are different, and in the context of an application it's... well, not necessariliy good, but normal. Runtime warnings and unexpected input is at least input the programmer is aware of. I'd be more scared of an app that lacks runtime warnings.


Yep, but regarding my remark I actually meant both.


Warnings and static analysis tools are good, but wrangling them across multiple tools, architectures, and coding standards is hard. Really hard. Different compilers will throw warnings for different things. Linting gets crazy. A customer might ask you to run your code against a MISRA ruleset. A different customer may have their own coding standard. The two standards may contradict each other in areas.

Did you know that printf returns a value? Aggressive linting tools will throw a warning about an unchecked return value.

So is printf("hello world?\n") wrong? And (void)printf("hello world\n") right? I don't know anymore.


   >And (void)printf("hello world\n") right?
then of course, you find a tool that complains, "useless cast"


Serious question (I've been exactly the guy that planteen described -- wrangling code into submission for multiple static analysis tools and compilers, for warnings, MISRA compliance, etc.)

So my question: do you remember what tool would complain about such a construct (e.g. casting return value of printf() to void)? Reason I ask is because printf() returns a value; the cast to void is explicitly and deliberately -- right or wrong -- saying, "It's OK, I got this. I am choosing to ignore it." So a static analysis tool complaining about such a construct strikes me as a bit odd.


GCC's warn_unused_result function attribute still warns even if you cast to void. And that's unlikely to change. Here's the lastest thread on that.

  https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425
Older version of glibc tagged printf, etc, with warn_unused_result and it was extremely annoying. The defense was understandable but that it was eventually removed was the right decision.

There are a couple of other warnings I always explicitly disable, including -Wmissing-field-initializers and -Winitializer-overrides/-Woverride-init (clang/GCC).

I always use -Wall. I'd like to use -Wextra but it's problematic given that every few releases someone may introduce a new diagnostic that emits spurious warnings. When a nice clean build suddenly starts spitting out warnings you get a deluge of complaints and pointless patches at exactly the moment you have zero time to deal with them. So I usually stick to just -Wall and then run separate static analyzers (including clang static analyzer) before a release.

It doesn't help that with clang you now have twice the problems, and often twice the number of workarounds required. Notice above that disabling initializer override warnings requires a different option on clang than GCC. And while with recent versions of GCC and clang you can disable certain warnings using pragmas, you still need to special case each compiler

  #if __clang__
  #pragma clang diagnostic push
  #pragma clang diagnostic ignored "-Wmissing-field-initializers"
  #pragma clang diagnostic ignored "-Winitializer-overrides"
  #elif GNUC_PREREQ(4,6,0)
  #pragma GCC diagnostic push
  #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
  #pragma GCC diagnostic ignored "-Woverride-init"
  #endif
So much useless boilerplate....

And then there are the people who build your software with -Wextra regardless and complain when they get spurious diagnostics. IIRC, GCC only enables -Woverride-init with -Wextra whereas clang enables the similar check with -Wall. I explicitly disable it for GCC precisely because invariably people will try to compile stuff with -Wextra.

Given how much larger C++ is than C, I can only imagine C++ developers have (or will have in the future) many more headaches of this kind. (But not with initializer overrides specifically as C++ rejected named initializers, which is the only way to override initializers. It's one of the ways that C and C++ have irreversibly split.)


Well, hello there, fellow MISRA warning wrangler!

I think you're right and I usually find MISRA gives decent warnings and solving the warnings is quite instructive. I remember, for example, John Carmack had nice things to say about static code analysis.


Yep!


Which is why I tend to support languages with stronger type safety, although I also enjoy C++ a lot.

Type systems are not optional and are the same regardless of the compiler, vendor, customer or willingness to use them.


Your logging code should be wrapped up in a helper anyhow, so you can easily filter or send it to disk\network.


Add -Wdouble-promotion to the list. For microcontrollers which implement float in hardware and double in software, it can be a lifesaver.


That's a great hint, I was almost at the point of writing a short script that checks the binary output for any soft FP operations.


Yes, that's a very good idea, especially if code uses lot of floating point operations.


Author here - thanks! I left out advice like, "turn on all the warnings" because I was trying to focus on problems specific to embedded platforms. I'd strongly argue advice like that that is great regardless of what your target is.


> One thing I would add is: enable all the warnings you can and make them errors: "-Wall -Wextra -Wpedantic -Werror".

Note:

GCC (and probably other compilers) for Embedded systems don't accept 'void' return type for 'main' function without warning. As it is allowed by standard (ie, it can be implementation defined), and as it is normally used as return type for 'main' function in Embedded programming, the compilation may fail at 'main', even if the code is strictly conforming to standards.


My recollection is that 'void main' is illegal and always has been according to all versions of the C++ standard.

(I'll check when I'm no longer on a phone unless someone beats me to it!)

Further, to confuse the issue, you don't have to explicitly return anything from main even if it's defined as 'int main'.


From Standard docs., 3.6.1.2 Main Function,

It shall have a return type of type int, but otherwise its type is implementation-defined. All implementations shall allow both of the following definitions of main: int main() { / ... / } and int main(int argc, char* argv[]) { / ... / }

In the latter form argc shall be the number of arguments passed to the program from the environment in which the program is run.If argc is nonzero these arguments shall be supplied in argv[0] through argv[argc-1] as pointers to the initial characters of null-terminated multibyte strings

---

No version of the C or C++ specs have said void main is acceptable, (apart from the implementation-defined optional main for non-hosted systems). However, most compilers have it as a language-extension.

A return statement in main was compulsory until C99, where the compiler was to assume return 0, if not specified. Some compilers had that behaviour already, however.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: