Now that I got my head wrapped around the ‘C’ language to a point where I feel proficient enough to write clean code, I’d like to focus my attention on project architecture guidelines. I’m looking for a good resource that coves the following topics:
- How to create an interface that promotes code maintainability and is extensible for future upgrades.
- Library creation guidelines. Example, when should I consider using static vs dynamic libraries. How to properly design an ABI to cope with either one.
- Header files: what to partition out and when. Examples on when to use 1:1 vs 1:many .h to .c
- Anything you feel I missed but is important when attempting to architect a new C project.
Ideally, I’d like to see some example projects ranging from small to large and see how the architecture changes depending on project size, function or customer.
What resource(s) would you recommend for such topics?
Whenever I got serious writing C code, I had to emulate C++ features in it. The main stuff worth doing is:
Think of each module like a class. The functions you expose in the header are like public methods. Only put a function in the header if it part of the module’s needed interface.
Avoid circular module dependencies. Module A and module B should not call each other. You can refactor something into a module C to avoid that.
Again, following the C++ pattern, if you have a module that can perform the same operations on different instances of data, have a create and delete function in your interface that will return a pointer to struct that is passed back to other functions. But for the sake of encapsulation, return a void pointer in the public interface and cast to your struct inside of the module.
Avoid module-scope variables–the previously described pattern will usually do what you need. But if you really need module-scope variables, group them under a struct stored in a single module-scope variable called “m” or something consistent. Then in your code whenever you see “m.variable” you will know at a glance it is one of the module-scope structs.
To avoid header trouble, put #ifndef MY_HEADER_H #define MY_HEADER_H declaration that protects against double including. The header .h file for your module, should only contain #includes needed FOR THAT HEADER FILE. The module .c file can have more includes needed for the compiling the module, but don’t add those includes into the module header file. This will save you from a lot of namespace conflicts and order-of-include problems.