wiki1041: GeneralDeveloperInformation (Version 3) |
QNX has been developing software for a number of years, and coding techniques and styles have evolved over that period of time as technology has evolved ... and so we evolve with it. The QNX Coding Standard and the QNX Best Practices documents capture some of the lessons learned during the development of our software for multiple target architectures and multiple host environments. The documents are also continuously evolving as we gain new experience and introduce additional technologies. QNX Coding Standard#1. Introduction CONSISTENCY is at the heart of readable source code. Mixed coding style is harder to maintain than any particular "bad" coding style. When working on existing source code (GNU, Dinkum, QNX4, Photon etc), the particular format and style used by that source and its associated modules (headers, other source files) should be maintained regardless of this document. The intention is not to retro fit a new style onto all of QNX's existing source code base. For new projects this document provides a common set of C coding standards and recommendations that should be followed. Standardizing will provide consistancy, improve portability, reduce errors and help new employees/third parties and customers reading our source learn the "QNX way". 2. What To Do When It Isn't Specified This document provides a number of guidelines based on existing QNX source code and industry best practices; it does not cover every single case imaginable. When something is not specified (ie continuation behaviour of long function names or long argument lists) and there are no examples in the existing source to follow (Rule #1 Be Consistant) make a choice based on providing a readable result that you will be able to use consistantly. 3. File Organization There is no maximum length limit for files, but files with more than about 1000 lines are cumbersome to deal with. Lines longer than 79 columns are not handled well by all terminals and should be avoided if possible. The maximum line length is 132 columns. 3.1. Source File Naming Conventions Source file names are made up of a base name, a period and a suffix. File names must be lower-case, do not have spaces, contain only a single 'dot' and have meaningful names. Avoid using file or directory names that can cause problems for certain filesystems: CON, PRN, AUX, CLOCK$, NUL, COM1-COM9, LPT1-LPT9 should be avoided. When working with QNX DDK's the file names/directory organization should be adhered to. Some compilers and tools require certain suffix conventions for names of files. The following suffixes are required:
The order of sections for a program file are as follows: QNX License/Copyright Header (refer to section 7) Module Description (refer to section 4.8) #include Directives typedefs externs globals Source code .... 3.2.1. Order of Functions Within a File Deliberately arrange the order in which the functions are defined in a file so as to make forward declarations unnecessary and your source easier to maintain and follow. Typically, you do this by placing the function that calls most other static functions at the bottom of the source file and placing the functions at the bottom of the caller/called hierarchy towards the top of the file. 3.3. Header Files Header files should keep the number of other headers they include to the bare minimum, subject to other rules - see below. 3.3.1. System Headers (i.e. headers files in /usr/include): The order of sections for are as follows: QNX License/Copyright Header (refer to section 7) Module Description (refer to section 4.8) #ifndef _HEADERFILE_H_INCLUDED #define _HEADERFILE_H_INCLUDED ... stuff in file ... #endifFor header files in sub directories use the following syntax: #ifndef _DIR_HEADERFILE_H_INCLUDED #include <DIR/HEADERFILE.h> #endifwhere: DIR = directory relative to /usr/include where the file will exist HEADERFILE = name of the fileThe special case of files in /usr/include/sys will have DIR = "" resulting in names such as HEADERFILE. For the special case of header files in /usr/include/sys #ifndef __HEADERFILE_H_INCLUDED #include <sys/HEADERFILE.h> #endifFor all type references in public header files other than those that are fundamental types, the file must include the appropriate definitions for those types. (If these definitions are in some other header files, thosefiles must be included enclosed by the appropriate #ifndef ... #endif blocks.) Whenever possible, public header files must avoid polluting the user name space with extra symbol names. This means that when using types, if there's a version of the typename in the implementer's reserved name space (leading underscore's), you should use that version rather than the public typename. All of the reserved name versions are defined by including <sys/platform.h>, so by including that file, you hopefully will be able to avoid including other headers and introducing more symbol names than necessary. E.g. do: #include <sys/platform.h> typedef _Uint32t my_type;rather than: #include <inttypes.h> typedef uint32_t my_type;In headers, function prototypes parameter names should all be declared so that they are prefixed by . This is just a safeguard against unexpected macro expansions for common names in parameters. When defining constants to be used in flags, make sure the constants clearly indicate how large the target flag variable is. i.e. if the target flag storage is 32 bits, add appropriately enough leading zeroes to the constant. Surround any typedef's or function prototype definitions in a public header file using: __BEGIN_DECLS /* typedefs and function prototypes go here */ __END_DECLSThe BEGIN_DECLS & END_DECLS macros are obtained by including <sys/platform.h>. New header files should as often as possible avoid installing to /usr/include to avoid pollution of the /usr/include directory with a lot of random stuff. Preferably, if the header files belong to a specific module, then the header could install to /usr/include/modulename (as a directory, with the actual headers under it). Existing module subdirectories: ppc, mips, x86, arm, sh CPU specific headers hw headers describing hardware stuff photon Photon headers3.3.2. Application Header Files: The order of sections are as follows: QNX License/Copyright Header (refer to section 7) Module Description (refer to section 4.8) #ifndef HEADERFILE_H_INCLUDED #define HEADERFILE_H_INCLUDED ... stuff in file ... #endifApplication header files should never use types defined in platform.h. They should instead use inttypes.h. E.g. do: #include <inttypes.h> typedef uint32_t my_type;rather than: #include <sys/platform.h> typedef _Uint32t my_type;3.4. Executable/Library File Naming Conventions Refer to requirements/design documents. 3.5. Installation Refer to Filesystem Hierarchy Standard (version 2.2 final) 3.6. Source Code Build & Layout Refer to Source Code Build & Layout document. 4. Specific Code Formatting Issues The following points address specific code formatting issues. CONSISTANCY with existing source formatting and style takes precedance over these guidelines. Running a formatting utility like indent over source code should be considered only as a last resort to restore sanity to source code that has been irreparably infected with multiple formats and styles. In this case it is suggested to use these guidelines for the re-formatting. 4.1 Indention The standard indention is four (4) spaces. 4.2. If...Else Statements The else clause of an if {...} else {...} should be "cuddled" as shown in this example: if (something) { /* Do some stuff here. */ } else { /* Do something else. */ }4.3. Case Statements Case statements should be "cuddled" as shown in this example: switch (number) { case 0: ... break; case 1: ... /* Fall through */ default: break; }Continuations from one case to another should be clearly identified with a comment as above. 4.4. For Statements For statements should be "cuddled" as shown in this example: for (cnt = 0; cnt < some_value; cnt++) { ... do stuff ... }4.5. While Statements While statements should be "cuddled" as shown in this example: while (some_condition) { ... do stuff ... } do { ... do stuff ... } while (some_condition);4.6. Format of Data Structures Do not make assumptions about the layout or padding of a structure or the allocated size of a data structure. These depend on the compiler implementation and can vary significantly with the type of target CPU. When defining a structure that will be used in message passing or will be written to external device (e.g a hard disk, serial line), the types of all the fields must be explicitly sized. eg: struct foo { int bar; }; is a no-no, while for a PUBLIC header (ie /usr/include) struct foo { _Int32t bar; };or NON-PUBLIC header struct foo { int32_t bar; };is correct. Note that even when a _Int32t style declaration is used the documentation should use the int32_t as the reference type. On the opposite side, do NOT use an explicitly sized type unless you really need it - internal structure definitions can get along quite happily without them and you don't limit yourself in the future when we're running on a 64-bit CPU (and that's going to be sooner than you think). It is also a good idea to plan for expansion when defining public data structures. ie struct foo { int bar; int rsvd[4]; };4.7. Data Alignment Align your data, especially structure elements, on their native boundaries. This means that shorts should start on any even address, longwords on addresses evenly divisible by four, and 64-bit values (quadwords) on addresses evenly divisible by eight. struct foo { int16_t type; int16_t rsvd; /* Explicit padding/reserved */ int32_t data; };4.8. Module Description Each source/header file should contain a block comment immediately after the license header that gives a short description of what the module does and (if not clear) how to use it. Discussion of non-trivial design decisions and side-effects is also appropriate. For example: /* * options.c * * This file contains code to parse command line options. */4.9. Function Description Each function should be preceded by a block comment. The comment should provides usefull information about:
The comment block should be formatted in a manner such that it is easy to read and automated documentation generation tools such as doxygen can extract meaningful developer information. Avoid dup-licating information clear from the code. One doxygen example format: /** * function_name A short synopsis of the function * - Description: This is a longer description of what * the function does. * - Arguments: * - param1: The first parameter description * - param2: The second parameter description * - Returns: * - Something that comes back */Real world example: /** * smc9000_parse_options: command line option processing * - Description: This function handles the special exception * cases in the general option processing for the SMC driver. * - Arguments: * - opt: a pointer to the command line options * - Returns: * - Option parsing status: EOK on success, EINVAL on failure */ int smc9000_parse_options( char *opt )4.10. Function Naming Public functions should be named using all lower case letters and underscores. 5. Comments C style (/* */) and C++ style (//) comments are allowed. The only excecption is that C++ style comments are not permited in public header files. Short/Single Line Comments Very short comments may appear on the same line as the code they describe, and should be tabbed over to separate them from the statements. If more than one short comment appears in a block of code they should all be tabbed to the same tab setting. if (a == EXCEPTION) { b = TRUE; /* special case */ } else { b = isprime(a); /* works only for odd a */ }Formatting Block Comments Format block comments as follows: /* * This is a block comment. Don't embellish these with lots of "stars * and bars." */Some Comments about Comments Comments must be in english and should tell the reader something non-obvious. A comment that repeats what is blindingly obvious is annoying at best. The following is an example: counter += 10; /* Add ten to counter */It is often better to aggregate comments about high-level and architectural issues in one place, to allow the reader and maintainers of your code to learn much more in a shorter time than if they had to piece together the issues and ideas about your features from comments strewn throughout several files. 6. Best Practices The QNX coding best practices can be found in QMS0026. 7. License/Copyright Headers: All source (*.c/cpp, *.h/cpp, *.s, *.S, etc) and common.mk's should have a license/copyright header at the top. You don't have to have license/copyright headers for usage files or "Makefile"'s - if the Makefiles are using the common makefile scheme (that is, they just include "recurse.mk" or "common.mk"). All "old" copyright headers (e.g. OCL, QCL, CSL etc) are deprecated. The only copyright header that should be put on is the standard QSS_Copyright_Notice. The mechanism for annotating source code with this license is to make use of the QNX custom RCS tags that will be automatically updated on source code checkout/checkin: $QNXLicenseC: $ : For use with C/C++ source files $QNXLicenseA: $ : For use with Assembly source files $QNXLicenseM: $ : For use with Makefile source files For example: /* * $QNXLicenseC: $ */Assembly file: # # $QNXLicenseA: $ #If additional (file-specific) text is required, these can be inserted in the same comment block but after the second "$" character. For example: /* * $QNXLicenseC: $ * * This is some additional information that is being added * as an extra part of this comment. */ |