Software Reuse In Embedded code

Yes, hence the popular name of the laws. Every time we get within sight of Disney (the company) losing the copyright on Mickey Mouse, Disney buys an update to the law extending copyright. Unless the USA changes its system of selling laws to rich corporations, or the rest of the world stops following them like poodles, then copyrights are now effectively unending.

Reply to
David Brown
Loading thread data ...

Exactly. Sometimes it's just clumsy/tedious to *not* resort to a goto.

OTOH, too tolerant an attitude towards them tends to result in code that gets tangled and gnarly. If I find myself "needing" to resort to a goto, it forces me to rethink the way I have built the algorithm.

OTOOH, note how often I "broke" MISRA rules in that code fragment. Rewriting it to consolidate all return's to a single point would have resulted in a real tanlged mess. Then, going back and removing the break's... etc.

Simply because it is too easy to fall into the "routine" of resorting to goto to fix the structural errors in your code. IMO, it's a case where you *discourage* its use but never

*forbid* it.

It's *easy* to hit 4 levels of indent in the body of a non-trivial function. Wrapping clauses in function calls just muddies the waters for the sake of eliminating an indent level. E.g.,

gobble_leading_zeroes( char **ptr // IN/OUT references pointer to digit string ) { p = *ptr while (*p == 0) p++

*ptr = p }

(or equivalent)

I rely on the "shape" of my code on the "printed" page to convey a lot of information about it's structure. And, I *obsess* over whitespace (there's never enough!).

E.g., the axiom that "a function should fit on a single page" just doesn't work for me -- unless you let me run the monitor in portrait orientation! :>

But that's the point!

"if ((*ptr != 'e') && (*ptr != 'E'))" is the opposite of "if ((*ptr == 'e') || (*ptr == 'E'))" and, IMO, is easier to relate to than prefacing the latter with '!'

I.e., "if it's not an 'e' and it's not an 'E', either, THEN..." instead of "if it's not: (an 'e' or an 'E') THEN..."

Use these equalities to express things in easier terms with which to relate.

Reply to
Don Y

True, I very rarely have functions with more than 1 label in them, and if I use a goto it usually follows a particular idiom that easy to understand. There's never a good reason to get really tricky with gotos.

Why does a function like that muddy the waters ? One glance at the function explains how it works, and one glance at the caller explains what the purpose is. The function name provides free documentation.

Although in this case, it probably doesn't help much, because the function is so simple, and the caller probably puts it in a bunch of easy to understand straight line code. With a slightly more complex function, it makes a lot of sense.

It nearly always works for me, unless the code is really straightforward, like a big case statement with simple cases.

Reply to
Arlet Ottens

Aaaaiiieeee!!

Steve

--
http://www.fivetrees.com
Reply to
Steve at fivetrees

I think you've bought all the vowels, Steve... would you like Vanna to try a *consonant*?

Reply to
Don Y

It doesn't add much *value* for what it does. I.e., it just cuts out one level of indent from appearing in its place (I was referencing the code sample I posted up-thread)

[the actual code is an eyesore because it uses up so much space on the page. But, trying to wrap portions of it in functions just kicks the can to other pages -- the code itself is so trivial that it doesn't "need" abstraction]

As I said, I like whitespace. So, lots of blank lines between stanzas. And, I like inserting "comment blocks" instead of tagging them on the ends of individual lines of code -- this quickly makes a function "longer".

Reply to
Don Y

I agree that in this case it doesn't make much sense to put this code in a function, but if you have a more complex function that needs 5 levels of indentation, using a function to bring that down to 3 levels usually does help to make it more readable. If you make the function 'static', and use it only once, the compiler will usually inline it, so it doesn't have any runtime cost.

Reply to
Arlet Ottens

There are any number of ways of avoiding this. One is to have an error flag; set it if you need to do cleanup (not great). Another is to return FALSE early if errors are encountered (see below; preferred).

For me, using a goto is an admission of failure (to find a cleaner way), and I've not done this in 30+ years. There is *always* a better, cleaner, more comprehensible, and more robust way.

I have a code-purity fascist at work ;). I love him dearly, as he's even more OCD than me. If I need a sanity check on something I'm doing, I run it past him. If he were to object to something I'm doing, it would make me think hard... but this hasn't happened yet.

So... recently I ran something I do a lot past him. I had validation code that (simplified) looked like this:

if ( ! first_bit_of_data_valid() ) return( FALSE ); if ( ! second_bit_of_data_valid() ) return( FALSE ); ... some processing... if ( ! processing_happy ) return( FALSE ); return( TRUE );

These "early returns" get objected to (by n00bs, mainly) on the basis that some see them as glorified gotos; I don't. I see them as structurally sound, since they all wind up in the same place (a return).

Consider for a second the indentation levels of multiple tests for validity without early returns - every time you do a test, you indent one more time. (I have worse examples: imagine maintaining time of day, then date.)

So anyway, my OCD colleague thoroughly approved ;).

Friend, I lost you right there. *Simplifying the logic can only help readability*. I suspect you're fairly new to this game (which I'm not) and have a few things yet to learn (as do we all). That's cool; don't be offended and try to keep an open mind about better ways. I've spent most of my career trying to find ways of keeping out of trouble (and being able to sleep nights) - that doesn't make my approach perfect (e.g. I avoid C++ at all costs, much to the scorn of my code-purity fascist colleague [I prefer good *design* over good *language*), but I do sleep nights [1].

Steve [1] Except tonight. Too bloody hot. Am sitting by the aircon.

--
http://www.fivetrees.com
Reply to
Steve at fivetrees

(sigh) I'd have rather watched Vanna twirl some consonants...

Booo! Then every conditional after that has to include "!error &&" (assuming you *don't* want to execute the code protected by the conditional if you've previously detected an error)

But, if error means some form of *recovery* is required, that means you have to wrap this portion of the code in a separate function -- so that the caller can examine that error return and unwind things, as appropriate. If doing that requires accessing something referenced *within* the called function (the one that "error returned"), then you have to make that data accessible to the calling function, etc.

I don't think I would agree with that "unconditionally". I figure Ritchie, et al are pretty smart folks and didn't leave that feature in the language "casually".

OTOH, when they created Limbo, the noticeably *removed* the "goto".

OTOOH, in creating Limbo, the gave "continue" and "break" an optional argument -- an identifier indicating *which* enclosing "loop" is being referenced (in effect, where to "go to" as a result of the statement's execution).

[this must give the MISRA folks fits! continue, break *and* goto all cohorting together!! :> ]

I don't think anyone is questioning the *need* for "goto". But, rather, that ruling it out can make code clumsier (e.g., the Limbo break/continue fits my style of wrapping complex conditionals in do-while()'s and breaking, accordingly)

This is equivalent to the example I posted.

The problem comes in when what you really want is:

if ( ! first_bit_of_data_valid() ) { cleanup_mess_from_first_bit(params1); return( FALSE ); } if ( ! second_bit_of_data_valid() ) { cleanup_mess_from_second_bit(params2); cleanup_mess_from_first_bit(params1); return( FALSE ); }

etc.

Sure, you can interpose another function to catch the "FALSE" return value... but, that function needs to be able to access all the stuff that the "cleanup" routines access -- potentially in the context of the "false-returning function".

At some point, this just gets clumsy and forces you to go out of your way *just* to avoid a goto.

Exactly. This is the case with the arbitrary precision decimal arithmetic package I alluded to previously. The code isn't overly complex -- "tedious" is a better term (lots of care to be sure you don't have fence-post errors, etc.). Couple that with my preference for informative (i.e., *long*) identifiers and every level of indent starts to hurt. Especially as it forces same-line comments further and further out (making them shorter and less effective -- hence my preference for comment blocks)

I will often rewrite conditionals to exploit things that I know to be true of the environment in which the code will operate. E.g., something that is more likely to affect a conditional one way or the other might get "promoted" to the front of the expression -- or, pulled into a separate expression "enclosing" the other expression (to emphasize that it is more significant to the algorithms performance). It's a minor efficiency hack but often can have rewards in loops, etc.

Pish! 30C? It's *40*C at 9P, here. High of 45C today. And tomorrow. (and I plan on being outdoors much of tomorrow :< )

Of course, unlike your 88%RH, we're sitting at something like *seven* %.

Reply to
Don Y

Except that 'return FALSE' doesn't work in the case above where you also need to call 'cleanup_stuff()' unless you duplicate the cleanup code, which is even uglier than error flags.

Sometimes simplying the logic can help to increase readability, sometimes it doesn't. For the same reason, I sometimes write

if( p )

and at other times I write:

if( p != 0 )

Sometimes I write: if( a && b ), and sometimes I write:

if( a ) if( b )

It all depends on the circumstances, and "how it reads". It's not necessary the case that the simplest logic is the easiest to read, although usually it helps, and in that case I obviously simplify the logic.

Yeah, I guess 25 years is fairly new.

Reply to
Arlet Ottens

Ah.

Where are you?

Steve (Will respond to rest of hugely interesting/challenging post when/if I get time.)

--
http://www.fivetrees.com
Reply to
Steve at fivetrees

Ah. Consider humble pie consumed ;).

I should know better. I'm working with a chap (hi, Mike!) whose work I respect hugely. His code and my code will likely never approve of one another ;).

Steve

--
http://www.fivetrees.com
Reply to
Steve at fivetrees

Arizona, USA.

never mind that -- get back to WORK!!! (slacker...) :>

Reply to
Don Y

[fanfare]

*Core Wars*!!!

Reply to
Don Y

g

se

e
n
.

c.

ha, I deal with those "simplifiers" everyday, simplifying sometimes removes much insight and physical meaning to the algorithm your trying to implement. A algorithm that natually has 8 logical steps to it should be left with those 8 steps, even if it can be reduced to 4 cryptic steps and still give you the same answer. Because when that algorithm needs to be modified or debugged those 4 steps are going to have to unrolled into those 8 steps. I believe it's a carryover from the olden days when saving every byte of source code counted. C language itself is a perfect example of that mentality, trading too much simplification for readability.

Reply to
steve

I can only agree.

I'm not arguing for logic reduction for its own sake; if it hinders readability, fergin fergit it.

However, I see code (regularly) that is *way* more complex than it needs to be, simply because of a lack of factoring - there's loads of duplicate code (and testing) because the coder is looking for conditions within conditions within conditions, and then those same conditions within conditions within *other* conditions, all of which could have been reduced to a few *clearer* set of tests by way of e.g. a bit of thought and a Karnaugh map...

Steve

--
http://www.fivetrees.com
Reply to
Steve at fivetrees

It depends on *how* (and what) you are simplifying. For example:

if ( (man) || (woman && pregnant) )

*clearly* is as easy to understand as

if ( man || pregnant )

If you look at a lot of conditionals, particularly, you can see where the person writing it just didn't *think* about what he was doing. It's as if he laid out each individual possibility "in a vacuum" without concern for the stanza of code immediately preceding, etc.

Granted, some compilers can remove this redundancy. But, it's presence increases the chance of typographic errors creeping in (e.g., "if (sex = MAN)" instead of "if (sex == MAN)" or "if (sex != WOMAN)", instead) as well as adding a lot to clutter. I *really* wish we had multicolored parens (no, not *colorized* but actually inherently different "characters" -- like brace, bracket, etc.) to make it easier to write and parse complex conditionals. [which is why I resort to the style of the first example]

Agreed. Unless those steps are obvious in the resulting code. E.g., foo += 4 vs four instances of foo++;

I always found: "Make everything as simple as it can be -- but no simpler" an amusing axiom. (on a par with "You always find the thing you seek in the *last* place you look")

Reply to
Don Y

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.