EmbeddedRelated.com
Forums
The 2026 Embedded Online Conference

Languages, is popularity dominating engineering?

Started by Ed Prochak December 12, 2014
On 12/12/2014 8:30 PM, John Speth wrote:
> On 12/12/2014 4:43 PM, Les Cargill wrote: >> - The Big Loop is honorable. > > Your list surely shows your hard earned battle scars. I've been on the > losing side of all of them at one point in my career. > > I just don't understand the big loop is honorable item. Are you saying > the big loop is good or bad? I've never seen a good big loop. I've > seen some scatter-brain type main loops that are hundreds of lines and > having absolutely no structure (bad). They were written mostly from > lack of experience and design. All the good loops I've seen or written > are a dozen or so lines long with good structure and good system > considerations designed in.
It's funny, but a lot of what you two posted reads like the guide to programming in Forth. -- Rick
Les Cargill <lcargill99@comcast.com> writes:
> The subset of 'C' you really need is rather small:
[interesting list, some comments]
> - Resource Acquisition Is Initialization. Holds for 'C', too. Use > ternary operators or "constructors" to achieve this.
I don't understand this: RAII is a C++ idiom that relies on C++'s exceptions calling object destructors in case of abnormal return from some lower level of the program. How do you do something comparable in C?
> - Only use "for" loops for integer-index loops. > - Use while (...) { ... if (...) break; } for everything else.
Hmm, ok a lot of the time, though idioms like for (p = list_head; p != NULL; p = p->next) { ... } seem perfectly fine.
> - Early Return Is The Right Way; enumerate and prioritize constraint > testing in this way. Happy path at the bottom...
OK, but what do you do with the return code in the error case?
> - Serialization of internal state is the Path to True Enlightenment
This is interesting and I haven't seen it put like that before. I'll give it some thought. A currently trendy practice (functional programming) is to minimize internal state and localize it to the extent possible, segregating stateful from stateless functions using the type system in the case of languages like Haskell.
> ( properly factored code cannot be understood statically ).
What do you mean by that? It sounds like the way OOP obscures control flow.
> - Callbacks rule when you need variant behavior.
You mean instead of an old fashioned switch statement?
> Serialization of callback state is part of the Path of True > Enlightenment.
Not sure what you mean by that.
> - Only allocate utility counters ) i, j , k ) on the stack. Use static > for everything else you can.
Why do you say this? Just to avoid having to analyze stack depth?
John Speth wrote:
> On 12/12/2014 4:43 PM, Les Cargill wrote: >> - The Big Loop is honorable. > > Your list surely shows your hard earned battle scars. I've been on the > losing side of all of them at one point in my career. > > I just don't understand the big loop is honorable item. Are you saying > the big loop is good or bad?
neither.
> I've never seen a good big loop.
I have. And I have seen it bad.
> I've > seen some scatter-brain type main loops that are hundreds of lines and > having absolutely no structure (bad).
They weren't finished. People generally don't finish things because of ... reasons. We don't want to know those reasons. Well, usually.
> They were written mostly from > lack of experience and design.
Sure.
> All the good loops I've seen or written > are a dozen or so lines long with good structure and good system > considerations designed in. >
Yep. Okay, so what I mean by The Big Loop works out to "I don't have a Real O/S (tm) so I will repetitively call function after function in a loop that should evoke nausea in a code review because it's so long because I don't have a real O/S but the functions all figure out how to manage what would be threads with a real O/S". You get the advantages ( and perils ) of run-to-completion this way.
> JJS >
Les Cargill <lcargill99@comcast.com> writes:
> "I don't have a Real O/S (tm) so I will repetitively call function > after function in a loop that should evoke nausea in a code review > because it's so long because I don't have a real O/S but the functions > all figure out how to manage what would be threads with a real O/S".
Start at slide #12: http://cufp.galois.com/2008/slides/HawkinsTom.pdf
On 12/12/2014 5:43 PM, Les Cargill wrote:
>> You're just supposed to know. And the guidance is there, but, well -- >> you're just supposed to know. > > The subset of 'C' you really need is rather small: > > - Only use "for" loops for integer-index loops.
I read this a couple of times and still am not sure how it was intended! :> - Don't use anything other than "for" loops for integer-index loops - Don't use "for" loops for anything other than integer-index loops :-/
> - Use while (...) { ... if (...) break; } for everything else.
In concert with the above... I use for, while and do as hints to the reader as to what the following code is likely to do and the criteria that govern its execution. E.g., "do" is best read as "do THIS until (something is no longer true)". It implicitly tells the reader "this WILL execute at least once. Read through it and you'll see (at the end) whether it will execute again." By contrast, "while" signals that the user should consider the driving condition *before* the code is executed -- "this *may* execute one or more times OR NOT AT ALL!"
> - Don't use switch () too much. > - Don't use else too much.
I use switch a lot! But, the trick is not to clutter up the N-way case's with lots of code -- which causes the switch to "disappear" in all the detail. Else finds frequent use replacing small switch's: if ... elseif ... elseif... Braces really help sort out nesting on if-else.
> - Early Return Is The Right Way; enumerate and prioritize constraint testing in > this way. Happy path at the bottom...
Early return regardless of success *or* err-out! The idea of forcing the exit from a subr/function to always be in one "return" statement is too arbitrary. And, often leads to extra nesting levels *just* to create this artificial structure (e.g., "do { ... break; ... } while (1)").
> - Separate constraint testing from processing.
Add invariants everywhere they logically exist.
> - Threads make everything worse.
Ah, I beg to differ. Doing two different things simultaneously WITHOUT threads quickly becomes brittle. ("Oh, gee! I forgot to blink the LED while I was in this bit of code...") But, good partitioning requires forethought. Just cutting a problem into multiple active entities isn't a panacea -- comms grows as the square (potentially)
> - Names matter.
But, artificial naming "standards" are ludicrous. Right maleAdultLes?
> - Be explicit and tolerate no ambiguity.
+42 Even though you "know stuff" (about the language, application, etc.) it can't hurt to put that in writing to make sure others also know it.
> - Tables are the One True Way.
I've taken this to an extreme in my current designs! I move the tables *out* of the executable and load them from a DBMS at run-time. This allows me to update the behavior of the code after deployment by tweeking DBMS contents. It also allows the code to modify aspects of itself in a more disciplined and structured manner. Finally, constraint checking on the table(s) -- in the DBMS -- makes the invariants that apply to those tables IN THE CODE more explicit (you can't update a DBMS table "wrongly")
> - Do not be afraid to declare static buffers for a single purpose.
I actually avoid this sort of thing. I want buffers (memory) to go away when not explicitly referenced. It also tends to complicate reentrancy and code sharing.
> - Only allocate utility counters ) i, j , k ) on the stack. Use static > for everything else you can.
See above. Eschew variables at file scope. Create (equivalent) types to clarify the nature of the object (even if the compiler won't be able to enforce strong type checking). Avoid "clever" code constructs as they don't inherently imply more efficient code. Let the compiler do most of the optimization. Browse the objects periodically to be sure WYSIWYG. Check all inputs -- especially from "outside the system" (e.g., user). Tie code to specification via appropriate commentary. [There are probably countless others on my "short list" but I've got cookies to attend to... :> ]
On 2014-12-13, Paul Rubin <no.email@nospam.invalid> wrote:
> Les Cargill <lcargill99@comcast.com> writes: >> - Only allocate utility counters ) i, j , k ) on the stack. Use static >> for everything else you can. > > Why do you say this? Just to avoid having to analyze stack depth?
I've done this myself in tightly constrained environments (8-bit MCUs) even though it goes against all my natural instincts for nicely modular coding with variables only defined in the scope(s) they are needed. In my case, pulling them out into .bss means it's easy to look at the linker map and see, at compile time, exactly how much memory is required for the variables making it far easier and reliable to analyze memory usage. Now having said that, I want to make it clear that in environments in which memory resources are not so tightly constrained (32-bit MCUs with several MB of memory available) my natural instincts are dominant and much more gets created (and passed) on the stack. Simon. -- Simon Clubley, clubley@remove_me.eisner.decus.org-Earth.UFP Microsoft: Bringing you 1980s technology to a 21st century world
On 2014-12-13, Don Y <this@is.not.me.com> wrote:
> > Braces really help sort out nesting on if-else. >
In my personal coding standards, _everything_ (ie: single statements) gets placed between braces in brace orientated languages.
> > Even though you "know stuff" (about the language, application, etc.) > it can't hurt to put that in writing to make sure others also know it. >
:-) My former employer for my day job (embedded work is a hobby for me) has just written in a reference that I like to document things. He's right. :-)
> > Browse the objects periodically to be sure WYSIWYG.
By this, do you mean using objdump and friends to give the generated code a once-over just to make sure you haven't done something that's hopelessly inefficient or invalid ? If yes, it's nice to see I'm not the only one who does this. :-) Simon. -- Simon Clubley, clubley@remove_me.eisner.decus.org-Earth.UFP Microsoft: Bringing you 1980s technology to a 21st century world
Am 13.12.2014 um 13:56 schrieb Simon Clubley:
> On 2014-12-13, Paul Rubin <no.email@nospam.invalid> wrote: >> Les Cargill <lcargill99@comcast.com> writes: >>> - Only allocate utility counters ) i, j , k ) on the stack. Use static >>> for everything else you can.
>> Why do you say this? Just to avoid having to analyze stack depth?
> I've done this myself in tightly constrained environments (8-bit MCUs) > even though it goes against all my natural instincts for nicely modular > coding with variables only defined in the scope(s) they are needed.
I'm afraid your reasoning is backwards in this case. It's precisely in memory-starved environments that you can not afford doing this. Making variables static when they don't need to be blows up memory consumption considerably. The stack isn't your enemy. It's the cheapest memory usage conservation technology there is, so use it.
> In my case, pulling them out into .bss means it's easy to look at the > linker map and see, at compile time, exactly how much memory is required > for the variables making it far easier and reliable to analyze memory > usage.
The problem is that it doesn't just make memory consumption easier to see ... it also makes it larger than it needs to be. So there's a good chance you'll run out of memory _because_ you wanted to figure out if/when you run out of memory.
On 2014-12-13, Hans-Bernhard Br&ouml;ker <HBBroeker@t-online.de> wrote:
> Am 13.12.2014 um 13:56 schrieb Simon Clubley: >> In my case, pulling them out into .bss means it's easy to look at the >> linker map and see, at compile time, exactly how much memory is required >> for the variables making it far easier and reliable to analyze memory >> usage. > > The problem is that it doesn't just make memory consumption easier to > see ... it also makes it larger than it needs to be. So there's a good > chance you'll run out of memory _because_ you wanted to figure out > if/when you run out of memory.
OTOH, it's a lot better than having to deal with subtle memory trashing errors because your now larger stack has descended into the space allocated to .bss (or even .data) and you find out the hard way that your code is now too big for the resources on the MCU you are currently using. I prefer to try and find out at compile time if the available resources are insufficient rather than have to find out the hard way at run-time. I accept what you say about the memory size increasing may be true in some cases, but if you are close enough to a resource boundary for this to make a difference, then maybe it's time for a larger MCU anyway. After all, code doesn't always remain static and quite often has functionality added to it, so you may hit the resource limit even with your stack based approach anyway. I suppose the major thing driving me here is to use development techniques which allow me a better chance to find out about these potential issues in a controlled deterministic manner as early on in the development process as possible. OTOH, as a hobbyist, I'm not churning these devices out by the thousands so there may be a cost tradeoff for you (in terms of using a cheaper less resource rich MCU that's a few pence cheaper) that simply doesn't exist for me. If that's true however, I would ask if the additional cost of your development time outweighs the savings from the cheaper MCU when you have to start debugging run-time resource availability issues. BTW, in my makefiles (especially the 8-bit target ones) there's a size command executed as part of the makefile target so I can see how the resources needed are increasing as I add functionality. It's a nice way to keep an eye on resource use without any additional manual effort. Simon. -- Simon Clubley, clubley@remove_me.eisner.decus.org-Earth.UFP Microsoft: Bringing you 1980s technology to a 21st century world
On 2014-12-13, Simon Clubley <clubley@remove_me.eisner.decus.org-Earth.UFP> wrote:
> On 2014-12-13, Hans-Bernhard Br&ouml;ker <HBBroeker@t-online.de> wrote: >> Am 13.12.2014 um 13:56 schrieb Simon Clubley: >>> In my case, pulling them out into .bss means it's easy to look at the >>> linker map and see, at compile time, exactly how much memory is required >>> for the variables making it far easier and reliable to analyze memory >>> usage. >> >> The problem is that it doesn't just make memory consumption easier to >> see ... it also makes it larger than it needs to be. So there's a good >> chance you'll run out of memory _because_ you wanted to figure out >> if/when you run out of memory. > > OTOH, it's a lot better than having to deal with subtle memory trashing > errors because your now larger stack has descended into the space > allocated to .bss (or even .data) and you find out the hard way that > your code is now too big for the resources on the MCU you are currently > using.
Making everything static creates all sorts of restrictions: you have to write seperate versions of functions for forground and interrupt use, you can't use threads, you can't use coroutines, you can't use recursion, etc. -- Grant
The 2026 Embedded Online Conference