camelCase vs wide_names
The wide_names style is used throughout the whole library code. There is no particular reason for this, it's just that I like it more ;-)
Prefixes and suffixes
Namespace
In order to avoid possible name clashes with other code, all exported symbols (to be more precise: the ones which are always exported, see below) of this library have to start with the nacore_ prefix, while all the #define directives declared in the public headers (and meant to be directly used by host and module developers) should begin with the NACORE_ prefix.
Visibility
This is a potentially tricky issue: the only way to be (almost) sure that a symbol is never exported, whatever OS/build system/machine used, is to declare it as static (and so use it only in that source file). In practice, though, it's not that simple, since it often happens that you want to access a "private" library function declared in another source file.
While on many build systems/OSes it is possible to use some specific keywords to export/hide symbols, this is not generally true.
To face such issue properly, the NASPRO core library follows a simple and portable approach, consisting of:
- a collection of macros to be used when declaring non-
static global symbols, wrapping system-specific visibility keywords (NACORE_PUBLIC_*() and NACORE_PRIVATE_*() macros defined in include/NASPRO/core/cpp.h);
- using the
_nacore_ prefix (notice the extra underscore at the beginning) for non-static global symbols (the ones declared with NACORE_PRIVATE_*()) which are not meant to be exported (this is because it's not guaranteed that they don't actually get exported) - the same goes for #defines which have to be included in public headers but are not meant to be used by hosts (the prefix becomes _NACORE_).
#defines
As you have certainly noticed, all #defined macro/constant names are written in uppercase. This is a widely used (and good) convention among C programmers.
typedefs, structs and enums
Again, the names of types which are not meant to be used by the host should begin with _nacore_, while in the other case they should start with nacore_.
There are only two more rules: all typedef names should end with the _t suffix and all typedefed function pointer types should end with the _func_t suffix.
Module symbols
All symbols to be retrieved in modules directly by the NASPRO core library should begin with the nacore_ prefix as well.
Prefixes should be avoided for…
static symbols and types in source files;
- local (inside a function's body) symbols;
- function and macro parameters.
Choosing names
Apart from the prefixing/suffixing rules described above, there are some other (more relaxed) conventions for choosing names properly.
First of all, it's important to point out what is considered to be important when naming something. Here's a priority ordered list:
- No ambiguity: names should be chosen so that their meaning is clear enough not to be confused in any way;
- Consistence: when a name for a given resource is chosen, everything else referring to that resource has to use that same name unmodified (for example if an array type named
nacore_array_t is defined, every name of function handling such arrays should contain array and not Array, arr, etc.);
- Predictability: make so that host developers can realistically guess names without directly knowing about them;
- Brevity: short names (when fully understandable and not ambiguous) are not only an improvement in readability, but also in performance (take a look at the "How To Write Shared Libraries" paper by Ulrich Drepper).
Let's get more in depth.
Types
Type names should be no longer than two words, since they're very likely to be used in functions as well.
Short, but well established and understandable, acronyms are allowed. For example nacore_rbtree_t is fine, while nacore_red_black_tree_t is not.
Variables
When naming variables is important to be informative and concise.
Following conventions used nearly everywhere (like single letters for counters or abbreviations like max and min) and reusing variables is ok.
As a general rule of thumb: there should be no need to comment on what a variable is used for (only exception being reuse).
Functions and macros
From a naming point of view, there are two kinds of functions: the ones which are strongly related to a type or a part of the library and the ones which are not.
In the latter case naming should simply follow the conventions already mentioned (examples: nacore_init(), nacore_exit()), while in the former case the first part of the name should recall the type/part involved and the rest is chosen as before (examples: nacore_modsys_load_module(), nacore_text_msg_report()).
Then, in order to have a good consistency and predictability, it is important to follow some naming patterns in some partciular cases. Precisely:
- instantiation function (with or without initialization) names should be in the form:
nacore_<optional_part>_<object>_new;
- freeing function (with or without finalization) names should be in the form:
nacore_<optional_part>_<object>_free;
- initialization function (without instantiation) names should be in the form:
nacore_<optional_part>_<object>_init;
- finalization function (without freeing) names should be in the form:
nacore_<optional_part>_<object>_fini;
- names of functions getting a certain value should be in the form:
nacore_<optional_part>_<optional_object>_get_<some_value>;
- names of functions setting a certain value should be in the form:
nacore_<optional_part>_<optional_object>_set_<some_value>;
- ...
The same rules apply to macros as well (obviously using uppercase names).