Archive for Month: November 2015
I’m hot blooded
Check it and see
I got a fever of 103
Well, not quite that high, but I’m in no condition to produce meaningful content at this point. Hopefully I’ll be able to double-post on Monday (assuming I beat this thing back).
Update: I didn’t beat it over the weekend.
Patterns: Creation and Destruction Stack
Virtually 100% of all memory leaks are preventable, assuming the programmer knows where to look. In many cases, the leaks occur because a programmer has failed to destroy what he has created, but it can become more complicated than that.
Creation Stack: The Order Matters
In many cases, we allocate memory as we use it, and we keep it at a simple layer. I’ve nearly lost track of how many calls to calloc() or malloc() I’ve hard-coded in the past few months alone.
This simple allocation is great, because it lets us dynamically generate variables and store them. These variables can be simple (like char or int), or they can be structures of varied complexity.
The most complex structures we allocate require us to allocate within the struct. Consider the following structure:
struct potato *next;
char * string;
This structure contains not one, but two pointers, and we need these pointers to point to something. Generally speaking, we would want to create a simple constructor that does something like this:
struct potato * make_potato()
struct potato * hi = calloc(sizeof(potato));
This would initialize a potato with null pointers and a size of 0. However, it’s usually more useful for us to fill in some values on construction of a structure like this:
struct potato * make_potato(int input_size, const char * something)
struct potato * hi = calloc(sizeof(potato));
hi->size = input_size;
hi->string = calloc(size * sizeof(char));
strncpy( hi->string, something, size);
In this case, we not only allocate a structure, but we allocate within the structure. This can get messy very quickly.
Destruction: Follow it up the stack
The rule to prevent memory leaks here is fairly simple: every destructor should be the inverse of its constructor.
If the constructor is simple, the destructor is equally simple:
int eat_potato(struct potato * hi)
However, when the constructor gets more complex, we have to treat it like a stack. That means that we should run every destructor in the inverse order of construction, so that we make sure to get everything freed before all pointers to it are lost.
int eat_potato(struct potato * hi)
Lesson: You build a building from the ground up, and you tear it down from the top. The same goes for structures.
Wednesday Post and New Book
I’m having some trouble getting my next post done, so I’ll have it up by Wednesday. Thank you for your patience.
On another note, I published my commentary on Ephesians to Amazon. If you or a friend is interested, search for “Ephesians in 6 Days” on Amazon (Price: $.99)
Patterns: Comment Through History
Programmers are not unlike Dory from “Finding Nemo”, in that we have a limited memory. It seems like every time we sit down to code, and we are randomly struck by a lightning bolt of inspiration, we immediately lose it when we notice something shiny.
That’s an extreme example, sure, but programmers do have a problem of forgetfulness. It’s the nature of the job – we have so many tasks to perform and so many paths to track that we can’t possibly hold on to all our thoughts.
Thank God for comments.
Level 0 Comments: In-line
This practice, so common to college-age programmers, is often lost quickly in the “real world”. However, these comments are perhaps the most useful comments we can have.
After all, which is faster: tracing all the calls and variables in a block of code, or reading a short sentence describing the intended function?
While I generally recommend you not write useless comments (“This printf() function prints a line of text”), there are several key things you can do:
- Outline your code BEFORE you start writing, then use that outline to walk your way back through it later
- Explain why you chose one function over another
- Describe the operation of a library-based function, so you don’t have to keep looking it up
- Leave TODO markers in your code (vi will highlight these specifically, so they’re easy to find again)
- Comment out a “bad” line, so that you can see what it did before the fix
- Leave some tag that’s easy to search for later (like TODO, only without the convenient highlighting)
All of these comments improve readability by restoring some of the mindset you had when you were writing the code in the first place.
Level 1 Comments: Flowerboxes
Less common, but equally important, are flowerbox comments. These comments allow the author of a piece of code to relay more detailed information in a compact, highly-visible format.
There are a number of uses for flowerboxes:
- Doxygen comments – these not only generate HTML documentation, but they also describe a function’s purpose, arguments, and return types inside of the code itself
- I cannot recommend Doxygen-style commentary enough
- Seriously, if you haven’t looked into it before, LOOK IT UP
- Flow descriptions – these comments describe a higher-level flow for a function or program, allowing the programmer to quickly get a deeper sense of how the program is supposed to work
- Disclaimers and Formalities – Want the world to know who designed the code, and what it’s for? Flowerboxes at the top of the page get it done
- Detail an event or conversation relevant to the code – Maybe an offhand quote from a fellow programmer inspired the design of the next segment of code. Recording that information helps future programmers understand not just what the code is doing, but why you chose to do it that way
Level 2 Comments: Logs
Some of my more recent work contains fewer comments than I usually employ. This is because, instead of using inline commentary to describe an event, I print out a string detailing what is supposed to come next.
These are still basically comments, because they serve the purpose of a comment while providing information during runtime. It’s a win-win.
Level 3 Comments: Code Segments
Sometimes (usually) we decide to replace whole sections of code with new code. However, when we do a delete-and-replace, we run the risk of damaging functionality with no way to roll back the source.
Using flowerboxes or #if statements, we can make sure that the old code is safely kept away from the final product while allowing us to restore that functionality if the time comes.
Also, it’s interesting to see how the code has evolved over time.
Level 4 Comments: Extra Documentation
Strictly speaking, everything that happens during the development of a piece of code should be documented. All conversations, whiteboard diagrams, emails, flowcharts, and other documents should be retained, if only so you can see what decisions were made.
Lesson: We put comment features into our languages for a reason. Use them liberally to spare everyone a lot of time and effort.
Patterns: Too Much Information
How many times have I seen this pattern in object oriented code?
- Public Class
- All members of the class are private
- All members can be accessed with accessor functions
- All members can be changed with modifier functions
I hate to break it to y’all, but that’s just a struct. The only thing you’ve done is add a hundred lines to a simple process.
Access Functions: Only What You Need
The purpose of an access function is simple: to provide access to a portion of the data, while hiding the structure of the class.
As such, it only makes sense to provide access functions in two cases:
- When it’s important to obscure how a structure works (due to complex operations, typedef abstraction, etc.)
- When it’s important to restrict how much information you reveal
In either of those cases, you only need enough functions to suit the need. If you’re exposing the entire structure to the world, there was no reason to make anything private in the first place.
Modifier Functions: Only what you want to change
If your structure or class contains constants you set only once, why would you create explicit modifier functions for it?
Or, if you always modify a set of values simultaneously, why would you create individual modifier functions?
Think about your design before coding up modifier functions.
Constructors: Two Types
There are two reasons to create an explicit constructor function:
- You have to allocate values inside of the new structure (a char *, for example)
- You want to fill the new structure with default values of some kind (perhaps by input)
In the first case, you usually call either another constructor or an allocation function inside of the constructor, and don’t worry so much about anything else.
In the second case, you have two options:
- Take no arguments, and fill in the variables with a general default value
- Take some arguments, and fill in some variables with specific values
Note that I don’t list among the options “Fill every variable at once”. While not inherently bad, this pattern shows up with alarming frequency in poorly-designed code. I recommend that you consider exactly how many variables you need to fill with a non-default value on initialization.
Destructors: Only One Type
Destructor functions should always follow the same pattern:
- Destroy any allocated values inside the structure
- Destroy the structure
If your destructor does anything else (besides, perhaps, print something), you should reevaluate your design.
Lesson: Think before you do. Lots of programmers over-code, resulting in functions they neither want nor need.
No post today, but a bit of advice
This bears repeating:
DON’T RETURN VOID.
Even when there’s no way for a function to fail, return an integer.
Because it allows the user to go on autopilot. If all of your functions return int (except for a few explicitly-named constructors – these can return the desired struct), the end user can just work your code into his error-handling framework without complaint.
When some return int, some return char *, some return uint8_t, and some return void, it makes the user think about how to deal with every function, adding complexity where it doesn’t need to be.
Complexity is the enemy. Keep it simple.