C++ Programming Practices

C++ Programming Practices

From TeleFlow

Jump to: navigation, search

This document defines the C++ coding practices, standards and coding conventions that TeleFlow Server programmers are requested to follow in order to retain continuity through the source code. This document may change from time to time, and it is hoped that programmers will stay informed of the most current version. This document defines the C++ coding practices, standards and coding conventions that TeleFlow Server programmers are requested to follow in order to retain continuity through the source code. This document may change from time to time, and it is hoped that programmers will stay informed of the most current version.

Borland C++Builder is currently TeleFlow's platform standard. As such, all code is expected to compile on Borland C++Builder 6 without errors or warnings until further notice. teleflow.org will probably be looking toward its community to select a standard compiler that will enable most of TeleFlow's functionality to compile on multiple operating systems.

Please note that TeleFlow Server is currently compiled with the Enterprise edition of Borland 6. This is required for Borland's XML object, which may not be required for the TeleFlow Open Source edition. Borland also has a free compiler which may help you get started faster.


Contents

Borland C++ Builder Project Options

1.1 Compiler
1.1.1 “Code optimization” sometimes interferes with the ACE libraries, and may need to be set to “none”.
1.1.2 “Warnings” must be set to “All” and the project must compile in Borland C++Builder without warnings. If warnings are created by the inclusion of 3rd party code, then the 3rd party code code should be enclosed with pragma statements to disable those warnings in the 3rd party code code and enable those warnings outside the 3rd party code code. e.g.
// Warning disabled because the NMS code is not to our standards
#pragma warn -8071
#include "..\3rdparty\nms\nccxsip.h"
// Warning re-enabled because our code is
#pragma warn +8071
1.1.3 “Treat enum types as ints” must be checked, otherwise exceptions can occur between modules.
1.2 Advanced Compiler
1.2.1 “Data alignment” must be set to “Quad word”
1.3 C++
1.3.1 “General” checkboxes “Zero length empty base classes” and “Zero length empty class members” must be unchecked.
1.4 Packages
1.4.1 “Build with runtime packages” must be unchecked, due to stability problems with the runtime packages.
1.5 Directories/Conditionals
1.5.1 “Intermediate output” must be set to “..\objs”
1.5.2 “Final output” must be set to “..\final”
1.5.3 “Conditional defines” must include “WIN32”

Formatting Considerations

2.1 Indents must be 3 spaces and tab characters must not be used. Borland C++Builder has a setting where the tab key generates a number of spaces instead of a tab character.
2.2 Parameterless functions/methods must use void.
int foo(void);
2.3 Naming
2.3.1 Use descriptive names, even in simple cases, such as using “int index” rather than “int i”.
2.3.2 Variable names should start with a lowercase letter.
2.3.3 Function, Class, and Method names should start with an uppercase letter.
2.3.4 “#defines”, enumerations, and constants should use all uppercase with underscores to separate words.
2.4 Place variable declarations at the start of the code block
2.5 Use code blocks to limit variable scope.
e.g. If a variable is used as a counter only inside a for loop, than declare it as such:
for(unsigned int counter = 0; counter < maximumCount; counter++)
{
   ...
}


2.6 Do not use macros except as provided by 3rd party APIs.
2.7 For comments, use “//” instead of “/*” and “*/”, even for lengthy sections. This is so that long sections of code can be commented out using “/*” and “*/” without interference.
2.8 Put using statements immediately below the #include statement being referenced. e.g.
#include <string>
using std::string;
2.9 Use braces for all branch blocks, including cases, even if the block has only one line. e.g.
if(length > 1024)
{
   length = 1024;
}
The only exception to this, is for switches with very simple cases. e.g.
switch(someValue)
{
   case OPTION1: phrase = phrase1; break;
   case OPTION2: phrase = phrase2; break;
   case OPTION3: phrase = phrase3; break;
   ...
   default: phrase = defaultPhrase;
}
2.10 Long Lines
2.10.1 Code lines should not extend significantly past the 80 character boundary
2.10.2 Where code lines are longer than 80 characters, breaks should be made between logical groupings, must not split types and variable names, and must be indented.

Header Files

3.1 Put all implementation code in .cpp files rather than header files except for in the case of templates where it is necessary.
3.2 Each function or method in a header file should include documentation describing functionality, return values for success and failure, effects of parameters, and user requirements.

Code Generation Requirements

4.1 Although the use of “std::string”s from the STL library is heavily encouraged, “std::string”s cannot be safely passed across DLL boundaries. “const char*” must be used instead.
4.2 All classes must have a virtual destructor. Lack of a virtual destructor can lead to exception errors in members that have destructors and in derived classes.
4.3 All conditionals must evaluate to boolean expressions. When checking a pointer for validity, use “(somePointer != NULL)” rather than “(somePointer)”.
4.4 Boolean operators must only be used on booleans, including '!'. When checking a number for a zero value, use “== 0” rather than negation.
4.5 Pointers must be set to NULL rather than 0. The only permittable exception to this rule is the declaration of pure virtual methods in a class where it is standard in the industry.
4.6 Although it is good practice to initialise pointers to NULL, since Borland C++Builder generates warnings when this is done, regretfully, pointers must not be intialised to NULL.
4.7 Any “char*” that is not being used for write access should be “const char*”.
4.8 Do not use "using namespace std;". Specify any std namespace types or classes where the header file is included. e.g.
#include <string>
using std::string;
4.9 Code must be written as platform-independent as possible. Where this cannot be, efforts should be made to isolate the platform-dependent code. Examples of platform-dependence follow:
  • Use of compiler-dependent code, such as Borland VCL or Microsoft MFC;
  • Use of operating system-dependent code, such as multi-threading mechanisms; The STL and ACE libraries can assist here.
  • Use of hardware-dependent code, such as byte order.
4.10 Commenting must be sufficient such that a first-time reader can understand the underlying methodology of the code in a reasonably short time.

Code Generation Recommendations

5.1 Enumerations should be used instead of “#define”s. Enumerations should belong to a class rather than the general namespace wherever possible.
5.2 “void*” parameters are not permitted in our code base unless there is an absolutely unavoidable reason for them. There should never be a case where where the type, struct, or class pointed to is unknown, however, should this be unavoidable, “const char*” or “char*” should be used instead of “void*”.
5.3 Friend classes are usually an indication of bad design. Before using them, look for alternatives.
5.4 Casting should only occur when working with class inheritance, deliberate conversion of simple types (such as short to int), memory manipulation such as reading from a buffer into a structure, or due to inappropriate typing in a 3rd Party API. Casting outside of these reasons should be checked for design flaws.
5.5 Parentheses should be used for keywords with parameters such as return and branch control, with the exceptions of new and delete.
e.g. “return(someValue);”
5.6 Parentheses for keywords, functions, and methods should be beside rather than spaced away.
e.g. “int foo(int someValue);” rather than “int foo ( int someValue );
5.7 Any method that does not modify the attributes of a class should be const.
5.8 Any integer variable that has only whole values should be unsigned.