Writing portable/reusable code
What are you priorities when writing code? Obviously you need to implement some specific functionality and there may be a required level of performance. This is all testable or measurable. However, there are two other key goals, that are harder rate:
- Write code that is clear and easy to understand. I shy away from using the term “self-documenting”, but that is certainly what you should be aiming for.
- Make sure that the code is maintainable and extendible. It might be you that has to do future work and a little care now will make your life easier down the line.
It is #2 that I would like to focus on today …
Designing code to implement some complex algorithms may be a challenging activity. Most developers enjoy that challenge – perhaps, if you do not, then you are in the wrong job. However, it is easy to get wrapped up in the details of the problem in hand and not look at the bigger picture. Writing code to solve a specific problem sounds just right. Obviously that will lead to the most efficient result, right? No, that is wrong.
By applying rule #2, the code will not be as specific, as provision for future enhancement/adjustment will have been made. This might make the code feel less efficient. But this is, in reality, very unlikely to be the case. A good compiler will generate optimal code for the job in hand. A simple example is the choice between a series of if…else… constructs as opposed to a switch statement. If you have this choice, go for the switch every time.
Another aspect of code design, that can lead to extensibility, with no loss of efficiency is to make is table- or data-driven. I propose an example:
Imagine that you are developing a ticket machine for a parking lot – a very typical embedded application. The machine takes some cash and delivers change. You need to have some code that works out the optimal way to deliver that change and might implement it like this:
void change1(int money) { printf("\nStarting money: %d\nChange:\n", money); printf("%d quarters\n", money/25); money %= 25; printf("%d dimes\n", money/10); money %= 10; printf("%d nickels\n", money/5); money %= 5; printf("%d pennies\n", money); }
Of course, the real code would activate the change delivery mechanism instead of the displaying the results with printf() calls, but this serves to illustrate the point.
As it stands, this code works fine. But what about extensibility? What is a 50 cent coin or even a $1 coin were to be introduced? [Although this sounds unlikely, no currency in Europe has a bill denomination worth less than about $7, as coins are more practical.] Adding the extra lines of code to address this would be quite straightforward.
Imagine that it is decided to ship the ticket machines abroad. In the UK and Euro countries there are eight coins. That would be more work, even though it would be possible. However, a redesign of the the function can make the change very simple indeed:
#define NUMBER_OF_COINS 4 int coin_values[NUMBER_OF_COINS] = { 25, 10, 5, 1 }; char *coin_names[NUMBER_OF_COINS] = { "quarters", "dimes", "nickels", "pennies" }; void change2(int money) { int i; printf("\nStarting money: %d\nChange:\n", money); for (i=0; i<NUMBER_OF_COINS; i++) { printf("%d %s\n", money/coin_values[i], coin_names[i]); money %= coin_values[i]; } }
Now, to change the code to work, say, in the UK, would only need the first three lines to be adjusted thus:
#define NUMBER_OF_COINS 8 int coin_values[NUMBER_OF_COINS] = { 200, 100, 50, 20, 10, 5, 2, 1 }; char *coin_names[NUMBER_OF_COINS] = { "£2", "£1", "50p", "20p", "10p", "5p", "2p", "1p" };
Another situation that would probably need to be accommodated is is stock of change. If a particular coin has run out, then it cannot be included in the calculation. Adding an array to keep track of how many of each coin is left and then checking/updating it would not be too difficult.
Another tweak would be to address the absence of the very small coins. Several Euro countries just do not use €0.01 and €0.02 coins; in the UK there is talk of eliminating the penny. Some kind of round-up or or -down mechanism would be needed. Again, not too challenging.
A personal note: I remembered, while writing this posting, that the very first program I ever wrote was to do exactly the same job as my example here. That was in 1976 and I used FORTRAN. Where did those 40 years go?
Of course, it goes without saying that some good embedded development tools are needed to generate efficient code and debug it effectively.
Comments
Leave a Reply
You must be logged in to post a comment.
I would suggest this (12 Steps to Better Code) : http://www.joelonsoftware.com/articles/fog0000000043.html
@Tomasz – Yes. That is interesting. Good advice.