Now clearly it’s highly likely that your compiler will complain if you attempt to pass a coordinate to a color and so on – and thus this is a definite improvement. However, nothing I’ve done here will prevent the X & Y ordinates being interchanged. Unfortunately, most of the time you are out of luck on this one – except in the case where you are dealing with certain sizes of display panels with resolutions such as 320 * 64, 320 * 128 and so on. In these cases, the X ordinate must be represented by a uint16_t whereas the Y ordinate may be represented by a uint8_t. In which case my COORDINATE data type becomes:
This will at least cut down on the incidence of parameters being passed incorrectly.
Although you probably will not get much help from the compiler, you can also often get a degree of protection by declaring appropriate parameters as const. A good example of this is the standard C function memcpy(). If like me, you find yourself wondering if it’s memcpy(to, from) or memcpy(from, to), then an examination of the function prototype tells you all you need to know:
void *memcpy(void *s1, const void *s2, size_t n);
That is, the first parameter is simply declared as a void * pointer, whereas the second parameter is declared as void * pointer to const. In short the second parameter points to what we are reading from, and hence memcpy is indeed memcpy(to, from). Now I’m sure that many of you are thinking to yourself – so what, the real solution to this is to give meaningful names to the function prototype. For example:
void *memcpy(void *destination, const void *source, size_t nos_bytes);
Although I agree wholeheartedly with this sentiment, I’ll make two observations:
- You are assuming that the person reading your code is sufficiently fluent in the language (English in this case) that the names are meaningful to them.
- Your idea of a meaningful label may not be shared by others. I’ve noticed that this is particularly the case with software, as it seems that all too often the ability to write code and the ability to put a meaningful sentence together are inversely correlated.
The final technique that I employ concerns psychology! Now one can argue that the failure to pass parameters correctly is due to laziness on behalf of the caller. At the end of the day, this is indeed the case. However, I suspect that in many cases, it’s not because the caller was lazy, but rather it’s because the caller thought they knew what the function parameter ordering is (or should be). A classic example of this of course concerns dates. Being from the UK (or more relevantly – Europe), I grew up thinking of dates as being day / month / year. Here in the USA, they of course use the month / day / year format. Thus when designing a function that needs to be passed the day, month and year, in what order should one declare the parameters? Well in my opinion it’s year, month, day. That is the function should look like this:
There are several things to note:
- By putting the year first, one causes both Europeans and Americans to think twice. This is where the psychology comes in!
- I’ve made the year signed – because it can indeed be negative, whereas the month and day cannot.
- I’ve made the month a MONTH data type, thus considerably increasing the likelihood that an attempt to pass a day when a month is required will be flagged by the compiler.
- I’ve made the day yet another data type (that maps well on to its expected range). Furthermore, attempts to pass most year values to this parameter will result in a compilation warning.
Thus I’ve used a combination of psychology and good coding practice to achieve a more robust function interface.
Thus the bottom line when it comes to designing function interfaces:
- Use strongly typed parameters.
- Use const where you can.
- Don’t assume that what is ‘natural’ to you is ‘natural’ to everyone.
- Do indeed use descriptive parameter names – but don’t assume that everyone will understand them.
- Apply some pop psychology if necessary.
I hope you find this useful.
Next Tip
An only vaguely related note about date order: YYMMDD is the usual format in Sweden (and I probably the rest of scandinavia). In this format, number order and chronological order is the same, which turn out quite practical on a computer.I saw once this comment in code I took over:// from a meeting with XXXX 2003-13-14go figure if it's january or februari 2004
.
I've been to Sweden probably six times in the last few years and I never noticed that you use the YYMMDD format. My apologies to Scandinavia! I think it does illustrate the perils of assuming something though!
Scandinavia is big endian, Britain is little endian, and the US is middle endian.If a function takes many parameters, putting them in a struct and passing a pointer is often more efficient (as well as being more robust) since it avoids copying the values on and off the stack, particularly if the same, or mostly the same, values are reused over many calls.