tags:

views:

979

answers:

8

Hi,

I have little more than beginner-level C skills and would like to know if there are any de facto "standards" to structure a somewhat complex application in C. Even GUI based ones.

I have been always using the OO paradigm in Java and PHP and now that I want to learn C I'm afraid that I might structure my applications in the wrong way. I'm at a loss on which guidelines to follow to have modularity, decoupling and dryness with a procedural language.

Do you have any readings to suggest? I couldn't find any application framework for C, even if I don't use frameworks I've always found nice ideas by browsing their code.

A: 

I'd suggets you to check out the code of any popular open source C project, like... hmm... Linux kernel, or Git; and see how they organize it.

Ivan Krechetov
+1  A: 

The number rule for complex application: it should be easy to read.

To make complex application simplier, I employ Divide and conquer.

MrValdez
+12  A: 

The key is modularity. This is easier to design, implement, compile and maintain.

  • Identify modules in your app, like classes in an OO app.
  • Separate interface and implementation for each module, put in interface only what is needed by other modules. Remember that there is no namespace in C, so you have to make everything in your interfaces unique (e.g., with a prefix).
  • Hide global variables in implementation and use accessor functions for read/write.
  • Don't think in terms of inheritance, but in terms of composition. As a general rule, don't try to mimic C++ in C, this would be very difficult to read and maintain.

If you have time for learning, take a look at how an Ada app is structured, with its mandatory package (module interface) and package body (module implementation).

This is for coding.

For maintaining (remember that you code once, but you maintain several times) I suggest to document your code; Doxygen is a nice choice for me. I suggest also to build a strong regression test suite, which allows you to refactor.

mouviciel
+3  A: 

The GNU coding standards have evolved over a couple of decades. It'd be a good idea to read them, even if you don't follow them to the letter. Thinking about the points raised in them gives you a firmer basis on how to structure your own code.

Lars Wirzenius
Not everybody likes them, from http://lxr.linux.no/linux+v2.6.29/Documentation/CodingStyle: "First off, I'd suggest printing out a copy of the GNU coding standards, and NOT read it. Burn them, it's a great symbolic gesture". I have not read them in many years, but Linus have some valid objections.
hlovdal
+5  A: 

It's a common misconception that OO techniques can't be applied in C. Most can -- it's just that they are slightly more unwieldy than in languages with syntax dedicated to the job.

One of the foundations of robust system design is the encapsulation of an implementation behind an interface. FILE* and the functions that work with it (fopen(), fread() etc.) is a good example of how encapsulation can be applied in C to establish interfaces. (Of course, since C lacks access specifiers you can't enforce that no-one peeks inside a struct FILE, but only a masochist would do so.)

If necessary, polymorphic behaviour can be had in C using tables of function pointers. Yes, the syntax is ugly but the effect is the same as virtual functions:

struct IAnimal {
    int (*eat)(int food);
    int (*sleep)(int secs);
};

/* "Subclass"/"implement" IAnimal, relying on C's guaranteed equivalence
 * of memory layouts */
struct Cat {
    struct IAnimal _base;
    int (*meow)(void);
};

int cat_eat(int food) { ... }
int cat_sleep(int secs) { ... }
int cat_meow(void) { ... }

/* "Constructor" */
struct Cat* CreateACat(void) {
    struct Cat* x = (Cat*) malloc(sizeof (struct Cat));
    x->_base.eat = cat_eat;
    x->_base.sleep = cat_sleep;
    x->meow = cat_meow;
}

struct IAnimal* pa = CreateACat();
pa->eat(42);                       /* Calls cat_eat() */

((struct Cat*) pa)->meow();        /* "Downcast" */
j_random_hacker
A pure C coder would get lost reading this code...
mouviciel
@mouviciel: Rubbish! Most C coders understand function pointers (or at least they should), and there's nothing really going on beyond that here. On Windows at least, device drivers and COM objects both provide their functionality this way.
j_random_hacker
My point is not about incompetency, it's about unneeded complication. Function pointers are common for a C coder (e.g., callbacks), inheritance isn't. I prefer that a C++ coder coding in C use its time to learn C than to build pseudo C++ classes. That said, your approach may be useful in some cases.
mouviciel
+2  A: 

If you know how to structure your code in Java or C++, then you can follow the same principles with C code. The only difference is that you don't have the compiler at your side and you need to do everything extra carefully manually.

Since there are no packages and classes, you need to start by carefully designing your modules. The most common approach is to create a separate source folder for each module. You need to rely on naming conventions for differentiating code between different modules. For example prefix all functions with the name of the module.

You can't have classes with C, but you can easily implement "Abstract Data Types". You create a .C and .H file for every abstract data type. If you prefer you can have two header files, one public and one private. The idea is that all structures, constants and functions that need to be exported go to the public header file.

Your tools are also very important. A useful tool for C is lint, which can help you find bad smells in your code. Another tool you can use is Doxygen, which can help you generate documentation.

kgiannakakis
A: 

Encapsulation is always key to a successful development, regardless of the development language.

A trick I've used to help encapsulate "private" methods in C is to not include their prototypes in the ".h" file.

Nate
+1  A: 

All good answers.

I would only add "minimize data structure". This might even be easier in C, because if C++ is "C with classes", OOP is trying to encourage you to take every noun / verb in your head and turn it into a class / method. That can be very wasteful.

For example, suppose you have an array of temperature readings at points in time, and you want to display them as a line-chart in Windows. Windows has a PAINT message, and when you receive it, you can loop through the array doing LineTo functions, scaling the data as you go to convert it to pixel coordinates.

What I have seen entirely too many times is, since the chart consists of points and lines, people will build up a data structure consisting of point objects and line objects, each capable of DrawMyself, and then make that persistent, on the theory that that is somehow "more efficient", or that they might, just maybe, have to be able to mouse over parts of the chart and display the data numerically, so they build methods into the objects to deal with that, and that, of course, involves creating and deleting even more objects.

So you end up with a huge amount of code that is oh-so-readable and merely spends 90% of it's time managing objects.

All of this gets done in the name of "good programming practice" and "efficiency".

At least in C the simple, efficient way will be more obvious, and the temptation to build pyramids less strong.

Mike Dunlavey