Next: , Previous: , Up: Rules When Writing New C Code   [Contents][Index]

8.2 General Coding Rules

See (sppm)Coding Style, is a good preamble for this section.

Every module includes <config.h> (angle brackets so that ‘--srcdir’ works correctly; config.h may or may not be in the same directory as the C sources) and lisp.h. config.h must always be included before any other header files (including system header files) to ensure that certain tricks played by various s/ and m/ files work out correctly.

When including header files, always use angle brackets, not double quotes, except when the file to be included is always in the same directory as the including file. If either file is a generated file, then that is not likely to be the case. In order to understand why we have this rule, imagine what happens when you do a build in the source directory using ‘./configure’ and another build in another directory using ‘../work/configure’. There will be two different config.h files. Which one will be used if you ‘#include "config.h"’?

Almost every module contains a syms_of_*() function and a vars_of_*() function. The former declares any Lisp primitives you have defined and defines any symbols you will be using. The latter declares any global Lisp variables you have added and initializes global C variables in the module. Important: There are stringent requirements on exactly what can go into these functions. See the comment in emacs.c. The reason for this is to avoid obscure unwanted interactions during initialization. If you don’t follow these rules, you’ll be sorry! If you want to do anything that isn’t allowed, create a complex_vars_of_*() function for it. Doing this is tricky, though: you have to make sure your function is called at the right time so that all the initialization dependencies work out.

Declare each function of these kinds in symsinit.h. Make sure it’s called in the appropriate place in emacs.c. You never need to include symsinit.h directly, because it is included by lisp.h.

All global and static variables that are to be modifiable must be declared uninitialized. This means that you may not use the “declare with initializer” form for these variables, such as int some_variable = 0;. The reason for this has to do with some kludges done during the dumping process: If possible, the initialized data segment is re-mapped so that it becomes part of the (unmodifiable) code segment in the dumped executable. This allows this memory to be shared among multiple running SXEmacs processes. SXEmacs is careful to place as much constant data as possible into initialized variables during the temacs phase.

Please note: This kludge only works on a few systems nowadays, and is rapidly becoming irrelevant because most modern operating systems provide copy-on-write semantics. All data is initially shared between processes, and a private copy is automatically made (on a page-by-page basis) when a process first attempts to write to a page of memory.

Formerly, there was a requirement that static variables not be declared inside of functions. This had to do with another hack along the same vein as what was just described: old USG systems put statically-declared variables in the initialized data space, so those header files had a #define static declaration. (That way, the data-segment remapping described above could still work.) This fails badly on static variables inside of functions, which suddenly become automatic variables; therefore, you weren’t supposed to have any of them. This awful kludge has been removed in SXEmacs because

  1. almost all of the systems that used this kludge ended up having to disable the data-segment remapping anyway;
  2. the only systems that didn’t were extremely outdated ones;
  3. this hack completely messed up inline functions.

The C source code makes heavy use of C preprocessor macros. One popular macro style is:

#define FOO(var, value) do {            \
  Lisp_Object FOO_value = (value);      \
  ... /* compute using FOO_value */     \
  (var) = bar;                          \
} while (0)

The do {...} while (0) is a standard trick to allow FOO to have statement semantics, so that it can safely be used within an if statement in C, for example. Multiple evaluation is prevented by copying a supplied argument into a local variable, so that FOO(var,fun(1)) only calls fun once.

Lisp lists are popular data structures in the C code as well as in Elisp. There are two sets of macros that iterate over lists. EXTERNAL_LIST_LOOP_n should be used when the list has been supplied by the user, and cannot be trusted to be acyclic and nil-terminated. A malformed-list or circular-list error will be generated if the list being iterated over is not entirely kosher. LIST_LOOP_n, on the other hand, is faster and less safe, and can be used only on trusted lists.

Related macros are GET_EXTERNAL_LIST_LENGTH and GET_LIST_LENGTH, which calculate the length of a list, and in the case of GET_EXTERNAL_LIST_LENGTH, validating the properness of the list. The macros EXTERNAL_LIST_LOOP_DELETE_IF and LIST_LOOP_DELETE_IF delete elements from a lisp list satisfying some predicate.

Next: , Previous: , Up: Rules When Writing New C Code   [Contents][Index]