Coding guidelines - Liquid War 6
Description: Liquid War 6

Next: Using the console, Previous: Compilation tips, Up: Hacker's guide

### 3.9 Coding guidelines

#### 3.9.1 Project goals reminder

One of the purposes of Liquid War 6 is to make a cleaner implementation of Liquid War than the previous one, namely Liquid War 5. While the latter has achieved the practical goal of providing a playable implementation of the game, it failed at providing an evolutive platform. Network capabilities where finally added to Liquid War 5, but anyone who played on Internet with someone a few hundreds of milliseconds away would agree that it's far from being perfect. The main reason for this is that it is really had to hack on Liquid War 5, especially when you are not the core developper. The core developper himself, even knowing all the various hacks in the game, is very quickly lost when trying to implement major changes.

To put it short, Liquid War 5 is a global variable hell, a pile of hacks on top of a quick and dirty implementation. Still, it works.

With Liquid War 6, the idea is to take the time to make something stable, something nice which will enable developpers to implement the cool features, and have fun along the way. Of course, this is only a dream, and in the (hopefully "very") long run, Liquid War 6 will also end up as a big unmaintainable mess, like any real-life program, until then, it should remain hackable.

#### 3.9.2 Common sense

Here are a few guidelines which I think are common sense advice, but they are still worth mentionning:

- try and respect the GNU coding standards;
- absolutely no `strcpy` or `sprintf` anywhere in the code. Nowhere. Use their equivalent `strncpy` and `snprintf` systematically, as they are part of the glibc and are an order of magnitude safer. Moreover, Liquid War 6 provides wrappers, such as `lw6sys_new_sprintf` which handles all the nasty dirty memory allocation stuff for you;
- keep global variables for when there is something truely global, and even in that case try to fit them in clearly identified structures.

#### 3.9.3 Unitary tests

Each of the internal libraries in Liquid War has a “test” program associated with it. For instance `liquidwar6sys-test` is associated to `libliquidwar6sys`, and its purpose is to test the features of this library.

While it is fairly easy to test out unitary functions which require no peculiar context, testing high-level functions which requires files, graphical and possibly network contexts to exist is obviously harder to achieve. There's no easy way to draw the line, but the idea is to put in these test executables as many features as possible, to be sure that what is tested in them is rock solid, bullet proof, and that one can safely rely on it and trust that code when running it in a more complex environnement.

These test executables are also very good places to see a library API in action, find code fragments, and make experiments.

#### 3.9.4 Memory allocation

Liquid War 6 provides macros to allocate and free memory. One should use them systematically, except when trying to free something allocated by another library, and in very special cases, mostly concerning low-low level operations which are seldom hacked on.

Usage of macros `LW6SYS_MALLOC`, `LW6SYS_CALLOC` and `LW6SYS_FREE` is straightforward, read any random chunk of code, for instance `./src/lib/sys/sys-test.c` to see them in action. They are defined in `sys/sys.h`.

Once used, these macros will track every single call to `malloc` and `free`, and if there's a difference, it will report it. It will also help you by showing what's in the non-freed memory area, at which line of code it has been allocated, and when. This is very usefull to track down memory leaks. Of course a debugger could tell you some of these informations, but experience shows than when you encounter a memory bug, it's very often impossible to reproduce it. So you one wastes time trying to reproduce the bug, whereas with this tool you have the information reported just when the problem happens.

#### 3.9.5 Private and public interfaces

Each library exports a public interface and hides its internal. Since Liquid War 6 uses standard C and no C++, there's no real standard way to handle public/private features. The convention used in Liquid War 6 is to show internal structures as opaque pointers (`void *`) whenever some function needs to operate on a structure which has possibly private fields. This way the caller function has no way to access the internals, and we are sure that no reference to any internal implementation specific feature will appear.

Here's a code excerpt from `src/gfx/setup.c`:

         void _lw6gfx_quit(_LW6GFX_CONTEXT *context) {
           /*
            * Implementation here.
            */
         [...]
         }

         void lw6gfx_quit(void *context) {
           _lw6gfx_quit((_LW6GFX_CONTEXT *) context);
         }

The function `_lw6gfx_quit` (note the “_”) is internal, declared in `internal.h` whereas the function `lw6gfx_quit` is public, and is therefore exported in `gfx.h`.

This way, functions in the program using `lw6gfx_quit` do not know what is in the `_LW6GFX_CONTEXT` structure, and they need not know it.

This does not mean it is not possible to have public structures, only these structures must reflect some truely public, accessible and safe to access structures.

#### 3.9.6 Commit policy

Basic rules :

- commits should be as small as possible, yet leave the code in a consistent state. The general idea is that big commits tend to make error tracking more complicated;
- a commit should not leave the code in a state in which it does not compile and/or is consistent from a user point of view. The general idea is to go step by step and break as little things as possible.
- tests should be written along the way. The project is not developped with a “test driven” method, still, it's a good practice to write the tests functions as soon as possible.

To check that a commit does not break everything, a good practice is to run a `make check` before committing / submitting anything.

Then, once it's within the main GIT repository, check the Jenkins builds to see if the program still builds correctly.

#### 3.9.7 Audit the code

Liquid War 6 is regularly audited with automated tools. You might need to pass `--enable-gcov` to `--configure` if you want to use thes tools yourself. More precisely:

- CUnit is used for regression tests. It's used to provide hopefully standardized output when testing, and provide test statistics more easily. It's a rule of thumb to try and write tests when new features and/or bug-fixes pour in.
- lcov is run, ideally with each release - but it's not garanteed, check the output date and time - and the output is available online on http://www.ufoot.org/liquidwar/v6/doc/coverage/.
- GNU global is run too, ideally with each release - again, check the output date and time - and the output is available online on http://www.ufoot.org/liquidwar/v6/doc/global/.
- pmccabe reports cyclomatic complexity. It shows where the code is too complex and should probably be rewritten. Output is post-processed using pmccabe2html from gnulib. The output is available online on http://www.ufoot.org/liquidwar/v6/doc/cyclo/.
- Valgrind is run as well, it should report absolutely no leak on all the core sub-libraries, eg running `liquidwar6ker-test` or `liquidwar6ker-pil`. Bits of code which depend on other libraries are a different story, for some projects on which Liquid War 6 depends might, for some reason, raise warnings. But as far as Liquid War 6 is concerned, the goal is simple: zero leak.
- Liquid War 6 is referenced on ohloh. Visit http://www.ohloh.net/p/liquidwar6 to get time-based statistics and facts about the source code.

Those tools certainly don't garantee the code is perfect, but they do help improving the quality of the program. If you hack, it's recommended you give them a try.