Monthly Archives: August 2019

Function names and “Clean Code”, part 2

See also: Part 1

Oh, the frustrations of obvious function names that don’t actually help.

LED1_On();
LED1_Off();
...
LED9_On();
LED9_Off();

First, it’s quite obvious that LED1_On() probably turns LED #1 on, and LED1_Off() turns it off. Simple.

But what is LED #1?

For most of my programming career, I have coded this way. You can always tell folks what that LED is in the commands… But then, of course, you don’t always do that everywhere it is used, and then if it is ever changed, all the old comments lie. (Thank you, Clean Code book, for making me fear comments.)

Today, I treat this kind of like how the OS-9 operating system treated devices… There is a device description (the thing that you are trying to access by name) and a device driver (the thing that controls the thing you are actually trying to access). Under OS-9, a hard disk might have been called “/h0” or “/h1”. A disk drive might have been “/d0” or “/d1”. The device drivers may have been some cryptic name with a chip number in it, such as “fd507” or “rb1003”. But that didn’t matter, because the used didn’t have to know and didn’t have to care. “/d0” was probably the main disk drive on any OS-9 system with a disk drive, regardless of what the actual underlying hardware was.

So for coding these days, I tend to write “low level” driver type functions like LED1_On() and LED2_Off(), and then wrap them in some clean code like:

void StatusLEDOn()
{
   LED1_On();
}

void StatusLEDOff()
{
   LED1_Off();
}

That adds a “useless” layer of code, but it keeps things nice and separate. If I see “StatusLEDOn()” in code, I can assume it probably turns on whatever the Status LED is. And if I see that function, I can determine that the Status LED must be LED #1.

And a good optimizer will likely just optimize these “useless” functions away, anyway.

Thanks to that stupid book, now when I see “ReadAI(2)” or “ToggleDO(5)” I just want to dive in and write wrapper functions to make that code look like “GetCurrentTemperature()” or “ToggleStatusLight()”.

I really need to go back and update all my code on GitHub some day…

Function names and “Clean Code”

At a previous job, I was introduced to the concept of “clean code.” As an embedded programmer, where we are often trying to squeeze extra bytes out of already optimized code, many of the principals of clean code do not apply.

But, in general, I like what I have read so far in this recommended book on the subject:

https://amzn.to/2KCtFy1 (affiliate link)

With this in mind, I wanted to ask a question about function names (whether they be in C, Perl, Java, or whatever).

In the past, I would write my functions using verb/noun, like this:

InitUserlog();
InitDisplay();
InitNetworking();

But after my exposure to object oriented programming, I learned about how classes and methods are used:

Userlog.init();
Display.init();
Networking.init();

This approach makes it very clean which class a function (method?) belongs to.

I started doing my C code similarly, letting the noun of the code lead:

UserlogInit();
DisplayInit();
NetworkingInit();

With all the Userlog functions starting with Userlog, I think it makes it clearer where things come from, and helps avoid collisions with outside code (or just code from another engineer on the same team).

With clean code, code should read more like English. Thus, using “InitializeUserlog()” is probably cleaner than “UserlogInit()”. But since you can’t do that with object oriented languages, perhaps writing backwards (Userlog.init(), UserlogInit(), etc.) is accepted due to the limitation of the language.

Indeed, “Userlog.lookupName()” seems less clean than “LookupNameInUserlog()”.

Perhaps I shouldn’t be doing this in C, since I *can* write the functions out more like proper English.

What do you think? Comments appreciated.

C and the dangers of memcpy()

Updates:

  • 2019-08-12 – Added note about using unions.

In the C programming language, memcpy (memory copy) is a function used to copy a range of bytes from one location in memory to another. I have used it often. Today, my boss mentioned something about not liking memcpy() because of all the dangers of using it. I understand that incorrect addresses, bad calculations, and simple omissions from a copy/paste can cause a memcpy() to have bad results. But, what’s the alternative?

He mentioned that he prefers to use C structures to copy data around, as opposed to using memcpy() to copy blocks of data.

Instead of doing this…

char Buffer1[64];
char Buffer2[64];

// Load Buffer1 with something.
strncpy (Buffer1, "Copy this string over", 64);

// Copy Buffer1 into Buffer2.
memcpy (Buffer2, Buffer1, 64);

That’s a simple, straight-forward example that seems hard to mess up. But doing something like this would be impossible to mess up:

typedef struct
{
    char Buffer[64];
} CopyStruct64;

CopyStruct64 Buffer1;
CopyStruct64 Buffer2;

// Load Buffer1 structure with something.
strncpy (Buffer1.Buffer, "Copy this string over", 64);

// Copy Buffer1 structure into Buffer2 structure.
Buffer2 = Buffer1;

Interesting. With properly defined structures, copying a block of data from one place to the other is done using code generated by the compiler.

(Update) And it seems I misspoke. He was actually talking about using unions, not structures. I’ve used unions as unions, but never for something like this. I did a similar test. (I realize there are several ways to declare a union, but I am using typedef so it looks similar to the above structure example.)

typedef union
{
    char Buffer[64];
} CopyUnion64;

CopyUnion64 Buffer1;
CopyUnion64 Buffer2;

// Load Buffer1 structure with something.
strncpy (Buffer1.Buffer, "Copy this string over", 64);

// Copy Buffer1 structure into Buffer2 structure.
Buffer2 = Buffer1;

Using union appears to work the same way, and since my example was not using structure elements, it seems to make more sense to do it that way. (End of Update)

I’m just now wrapping my brain around this, trying to figure out how this approach could be used to avoid using memcpy() and pointers.

What do you think? Am I on the right track to what he was talking about?

Until next time…