LibQxt Coding Style Guidelines 1. Files 1.1. Every header and source file begins with a header comment 1.2. Comments must be written in C++-format (// ...). Multiline comments may be written in syntax of C (/* ... */). 1.3. Header files 1.3.1. The file name extension of a header file is .h 1.3.2. A single public header file contains one public interface (class or namespace). 1.3.3. Header files contain only declarations. Implementation belongs to source files. - Exception: inline and template functions must be placed within header files because of compiler limitations. 1.3.4. Header files include only necessary header files. - Unnecessary include directives produce unnecessary dependencies between compile units. 1.3.5. Use forward declarations whenever possible. - A forward declaration is enough when a type is referred only as reference or pointer. 1.3.6. Header files do not contain using-clauses. - Using explicit namespace prefixes in header files lowers the risk of name conflicts. 1.3.7. Header files must be protected against multiple inclusion - Multiple inclusion of header files causes a compile error. #ifndef CLASSNAME_H #define CLASSNAME_H ... #endif // CLASSNAME_H 1.3.8. Include directives do not contain references to certain directories. - Using directory hierachies in include directives binds files strictly to certain environment and complicates portability. #include "../../module/header.h" // WRONG 1.4. Source files 1.4.1. The file name extension of a source file is .cpp 1.4.2. Source files include only necessary header files. - Unnecessary include directives produce unnecessary dependencies between compile units. 1.4.3. Source files include first local headers, and then external library headers. - This order assures that header files do not depend on external library headers and leads to easier portability. 1.4.4. Implementation of an interface is in the same order than in the header file. - This makes it easier to find the corresponding implementation. 2. Names 2.1. Comments and symbolic names are in English. 2.2. Symbolic names must not start with an underscore or contain two consecutive underscores. - ISO C++-standard reserves such names for compilers. 2.3. Names of classes, structs, namespaces and typedefs start with "Qxt". class QxtClass; 2.4. Names of variables, functions and member functions start with a lowercase letter. - Exception: constructors and destructors must be named after corresponding class QxtClass variable; void doSomething(); 2.5. Constants are written in uppercase. static const unsigned int BUFFER_LIMIT = 1024; 3. Variables and constants 3.1. Variables should be declared within innermost possible scope. - Variables declared close to where they are used improves code clarity. 3.2. Declare variables by separate clauses. char* p1, p2; // WRONG (p1 is a pointer, p2 is a char) 3.3. Variables must be always initialized. 3.4. Member variables are initialized in initialization list in order of declaration. - The initialization list is the only efficient way to initialize member variables. 3.5. #define directive must not be used for defining constants or functional macros. - In C++, named constants are implemented as const variables and functional macros are replaced with inline functions. 3.6. Constants are defined concentratedly in header files or at the beginning of source files. 3.7. Truth values are expressed as boolean values; true or false. - C uses zero and non-zero as truth values. These work in C++ as well, but are not recommended by ISO C++-standard. 4. Layout and style 4.1. Every programmer has his/her own programming style. The exact style does not matter as long as it is consistent per file. - This is important while applying changes to files which were originally written by somebody else. - Code looks awful and is unreadable if the programming style varies within a file. 4.2. It is troublesome to handle files of more than thousand (1000) lines. 4.3. It is not recommended to write functions of more than hundred (100) lines. 4.4. Pointer and reference characters are recommended to place to type name. - Pointers and references are considered as types. In addition, this leads to better readability: Result* result = 0; void printResult(const Result& result); 4.5. Indentation is done with spaces. - Tabulator characters are not recommended because different editors and viewers show them differently, often even unreadable. 4.6. An indented code block follows every if,else,while,for and do primitive. 4.7. Initial ({) and terminal (}) characters of code blocks must be clearly placed separately from code. The preferred way is to place them on separate lines. 4.8. Every switch clause contains a default branch. 5. Mixing C and C++ 5.1. Do not use exit() or abort(). - These functions stop execution without desctructing objects properly. 5.2. The return value of main() is int. - Defined in ISO C++-standard. 5.3. Do not use goto. - There are better control structures. Using goto leads to unreadable spaghetti code. 5.4. Use output streams instead of C IO functions. - printf() has to type checks. 5.5. Do not use malloc(), realloc() or free(). Use new and delete operators instead. - Old memory allocation routines know nothing about constructors and destructors. 5.6. Use extern "C" while including headers of C libraries. extern "C" { #include } 6. Type conversions 6.1. Do not use C-style cast. Use static_cast, dynamic_cast, reinterpret_cast and qobject_cast. - New type cast operators make sanity checks. 6.2. Avoid const_cast whenever possible. - Constant is an attribute bound at design time. There should be no need to remove constness. 7. Functions and member functions 7.1. Function parameters must be named. Function parameter names must match in both, function declaration and function definition. - Self-documentary parameter names are as important as verbose docs. 7.2. Object parameters must always be (const) references where possible. - Avoid unnecessary copying. - Implicit sharing is no excuse. Avoid unnecessary detaching. 7.3. Do not use a variable number of function parameters (ellipsis notation). - Complier does no checks to passed parameters. 7.4. Possible default values of parameters must be placed in function declaration only. - Possible default values must be clearly seen from header files. 7.5. Do not return a reference or a pointer to local data. - Local data of a function is no more existent after the function returns. 7.6. A member function must be a const function whenever possible. 8. Classes and objects 8.1. Objects are implemented as classes and records are implemented as structs. - A clear separation between structural data and objects. 8.2. A struct may have a constructor and (virtual) destructor but no member functions. - Records collect related data together. The only acceptable functionality is initialization and uninitialization. 8.3. Access levels are declared in order public, protected and private. - End-users are interested of public interface, which makes it natural to declare as first. 8.4. The visibility of class member variables is always private. - Exception: It is acceptable to use public variables in private implementation classes. 8.5. Class declaration does not contain implementation. Inline and template functions are implemented separately in the header file after the declaration. - End-users consider it easier to read clear declarations not cluttered with implementation details. 8.6. Avoid friend functions and classes. - Friendship breaks encapsulation. - Exception: A tight group of classes belonging together. 8.7. Retain semantics of overloaded operators. - For example, operator + does always an operation correspondent to addition. 9. Scope 9.1. Memory management 9.1.1. Ownership of dynamically allocated memory belongs to its allocator. Otherwise it must be clearly documented. 9.1.2. It is acceptable to use operator delete only to deallocate memory that was allocated by operator new. - Same thing with delete[] and new[] - Defined in ISO C++-standard. 9.1.3. It is acceptable to use operator delete[] only to deallocate memory that was allocated by operator new[]. 9.1.4. Nullify pointers after deletion. - Dereferencing floating pointers to already deleted memory tend to produce confusing error messages. 9.2. Constructing and destructing objects 9.2.1. It is preferred to provide at least one constructor for every class. - Default constructors provided by compilers are rarely usable. 9.2.2. You must not call virtual functions in class constructors or destructors. - Dynamic binding does not work in expected way when the object is in a half-baked state. 9.2.3. Do not use static (or global) variables in class constructors or destructors. - Initialization order of static variables is undefined. The object could itself be static (or global) whereas the static variable it depends on might not be initialized yet. Correspondingly, in destructor the referenced static variable might already have been destructed. 9.2.4. Constructor with one parameter must be marked as explicit. - Exception: Do not mark copy constructors as explicit. - This prevents compiler from using the constructor for implicit type conversion: QWidget widget = new QWidget(); // produces an error only because QWidget constructor is explicit 9.2.5. Classes with virtual functions must have public virtual destructors. 9.2.6. The destructor is responsible for freeing all resources allocated by the class. 9.3. Copying objects 9.3.1. It is preferred to provide a copy constructor for every class. - Default copy constructors provided by compilers might be non-suitable. 9.3.2. Inappropriate copying is prevented by declaring a private copy constructor. private: Q_DISABLE_COPY(ClassName) 9.3.3. An inherited class must always call base class copy constructor in the initializer list. - Ensures that the base class gets copied properly (what the default constructor might not always do). 9.4. Assigning objects 9.4.1. It is preferred to provide an assigment operator for every class. - Default assignment operator provided by compilers might be non-suitable. 9.4.2. Inappropriate assignment is prevented by declaring a private assignment operator. 9.4.3. An inherited class must always call base class assignment operator. - Note: Compilers do not do this automatically. 9.4.4. Assignment operators should skip assignment to self. - An unnecessary operation. Object& object::operator=(const Object& other) { if (this != &other) { ... } return *this; } 9.4.5. The return value of an assignment operator is *this. - Makes for example chained assignment possible. a=b=c; 10. Inheritance 10.1. Use only public inheritance. - Exception: Inheritance from Ui class in the multiple inheritance approach. 10.2. Inheritance is a static "is-a" relationship. Implement "has-a" relationship with member variables and type independent code with templates. 10.3. TODO: Do not misuse multiple inheritance. :) 10.4. Member functions of an interface class are pure virtual. - The purpose of an interface is to declare operations. Pure virtuality enforces declared operations to be implemented in subclasses. 10.5. An inherited class must always call base class constructor in the initializer list. - Ensures that the base class gets constructed properly (what the default constructor might not always do). 10.6. Inherited non-virtual member function is not recommended to be re-declared eg. shadowed in subclasses. 10.7. Default parameter values of inherited virtual member functions must not be redefined. 11. Templates and containers 11.1. Parameter type requirements of template functions must be clearly documented. - Error messages of template functions tend to be confusing. 11.2. Use type definitions to give sensible names to (at least complex) template containers. typedef QMap PhoneBook; 11.3. Make sure objets stored in template containers have suitable copy and assign operators. - Template containers handle stored objects by copying and assigning them. Resulting objects of copy and assign must be identical to original objects. 11.4. Use QtAlgorithms and/or STL algorithms instead of hand-made algorithms where possible. 11.5. The preferred way to iterate through a container is to use iterators. 11.6. Incrementing and decrementing an iterator is done with prefix operator (preincrement and predecrement). - Postincrement is less efficient, because it will have to return a copy of the old value. 11.7. Make sure not to use invalid iterators. - Non-mutable iterators get invalidated when a container is altered. 12. Exceptions 12.1. Never use exceptions in Qt code. - Qt isn't required to be compiled with exception support, and just by enabling exception support you invoke a performance hit -- even if you never use it -- so you can't rely on it being there. References: - http://www.cs.tut.fi/~oliot/kirja/tyyliopas/ - http://doc.trolltech.com/qq/qq13-apis.html Suggestions: [19:54:43] 13:54:43> i would prefer "Layout and style" beeing more strict [19:54:43] 13:55:04> "The exact style does not matter as long as it is consistent per file." thats a bad idea [19:54:43] 13:55:19> it should be consistent all over the entire prohect [19:54:43] 13:55:34> i think the GNU style or whatever it is called, we are currently using is all right [19:54:43] 13:55:37> with 4 spaces [19:54:43] 13:56:20> actually just removing that sentence is all right [19:54:43] 17:59:25> Suggestion for coding style: [19:54:43] 17:59:42> Use an enum in a namespace for related integer constants instead of const int or #define [19:54:43] 17:59:46> This is the Qt style. [19:54:43] 17:59:55> Er, in a namespace or class.