-
Notifications
You must be signed in to change notification settings - Fork 2
Coding Guidelines
Note: These rules do not apply to code generated by third party tools (ex. Lex & Yacc).
DO NOT exceed 120 characters in each line. However try to not exceed 100 characters, and go for 120 chars only if unavoidable.
DO use tabs to indent blocks.
DO NOT use /* ... */
style comments. Use only // ...
style comments.
DO use camelCase
style for naming variables, methods, functions and classes. Classes and
methods names should start in upper case.
// Bad
int n_trees;
void compute_total() {};
void computeTotal() {};
class my_class {};
class myClass {};
// Good
int nTrees;
void ComputeTotal();
class MyClass();
DO use Hungarian notation for variables referencing simple classes and types. Specifically, use the following prefix conventions:
Simple types:
-
n
forint
type -
b
forboolean
type -
c
forchar
type -
l
forlongint
type -
d
fordouble
type -
s
forALString
class andchar*
type -
li
forLoadIndex
class (rare)
Generic object:
-
o
forObject
class
Container types:
-
io
forIntObject
class -
do
forDoubleObject
class -
lo
forLongintObject
class -
oa
forObjectArray
class -
ol
forObjectList
class -
od
forObjectDictionary
class -
nkd
forNumericKeyDictionary
class -
ld
forLongintDictionary
class -
lnd
forLongintNumericKeyDictionary
class -
sl
forSortedList
class
Vectors:
-
cv
forCharVector
class -
sv
forStringVector
class -
iv
forIntVector
class -
lv
forLongVector
class -
dv
forDoubleVector
class -
liv
forLoadIndexVector
class (rare)
You may name int
with single letter names such as i
, j
and k
when they are iteration
indices.
DO use the Allman style for braces in blocks of code:
- The opening brace must be placed in the next line and at the same indentation level of the previous line
- The closing brace is at the same indentation level of the opening brace
- Code inside the block goes at the next level of indentation.
Example:
// Bad
while (x == y) {
// body
}
// Good
while (x == y)
{
// body
}
The header files (.h
) MUST contain the following sections (in this order):
- A
#pragma once
statement - The forward declaration of all the classes implemented in the corresponding
.cpp
file- The order is that of the definitions within the file
- All the necessary
#include
statements for the declared classes (ie. include what you use) - The code of all inlined methods and functions
Example:
#pragma once
class MyClass; // forward class declarations
class AnotherClass;
#include "Utility.h" // includes
#include "SomeDependency.h"
////////////////////////////////
// Class MyClass
// Provides "My" services
class MyClass: Object // class prototype definitions
{
// body
};
///////////////////////////////
// Class AnotherClass
// Provides "Another" services
class AnotherClass: Object
{
// body
};
/////////////////////////////////////
// inline methods and functions
inline int MyClass::SomeHeavilyUsedAccessor()
{
// body
}
Class MUST be declared in a header files. A comment banner MUST precede them explaining their purpose. A class declaration MUST contain the following sections (in this order):
- Public interface (
public
section)- Constructor and destructor declarations
- Public method declaration
- Implementation (
protected
section)- Banner
- Protected methods
- Parameters
- State variables/objects
Constraints:
-
DO NOT use
private
sections - DO NOT put method implementations in the class declaration
Example:
/////////////////////
// Class Example
// Showcases the style of a class declaration
class Example: public Object
{
public:
// Constructor
Example();
~Example();
// Context parametrization
void SetContext(ContextObject newContext*);
const ContextObject* GetContext() const;
// Access to count
int GetCount() const;
void SetCount(int n);
/////////////////////////////
//// Implementation
protected:
// Parameters
// Context
ContextObject* context;
// Work variables
// Count
int nCount;
}
private
sections may be used in low-level and/or tricky code.
Non inlined functions or methods MUST be implemented in a .cpp
file.
All local variables MUST be declared at the beginning of the method in a single block before any
code is executed, even their initialization.
Example:
void MyClass::DoSomething()
{
int i;
ALString sSomeWord;
ObjectDictionary* odMyDict;
SomeOtherClass* objectFromAnotherClass;
i = 0;
sSomeWord = "hola";
// Rest of the code
}
TODO
DO declare all class member object/variables as protected
.
- For class parameters DO implement both
Get
andSet
accessors. - Internal and work objects may implement a
Get
accessor.
Within the class or subclasses DO prefer accessors to interact with class members instead of doing it directly.
In the header comment every coherent block of functions related between them (e.g. getters and setters usually are commented as a block).
Classes must have a comment describing its functions and services
DO comment your code as if it were a document: A comment for each paragraph of code. A paragraph can be short if the method called is complex.
The C++ subset that Khiops uses is very minimal and most common C++ features and libraries are not allowed.
DO use const
whenever possible:
-
const
methods definitions to check at compile-time that they do not change the state of the calling instance -
const
arguments in functions/methods to check at compile-time that they are not modified by the method
An object is modified if it changes the value of one of its member fields or if
it calls a non-const
method.
Due to a technical difficulty, the containers' accessors methods such as ObjecArray::GetAt(int n)
return non-const
objects. So it may happen that you are unable to use const
is parts of the code
even if it is semantically correct.
DO use the override
keyword in the implementation of inherited methods. The compiler will
generate an error if the method does not exist in the base.
DO NOT use method overloading. Instead find specific names for functions and method.
// Bad
void MyString::Append(MyString sTail);
void MyString::Append(char cTail);
// Good
void MyString::Append(MyString sTail);
void MyString::AppendChar(char cTail);
DO NOT use C++ references.
Basic types (int
, longint
, double
, boolean
) are handled without pointers whereas complex
classes are handled with simple C pointers.
-
The
ALString
class which is handled as follows:- They are declared without pointers
- As function parameters they are passed as C++ references
- As functions return values:
- Use
const ALString&
if the called object have its memory responsibility (ex: accessors) - Use
const ALString
if the callee object have its memory responsibility (eg. Creator pattern)
- Use
-
The
KWSymbol
class also may use references.
DO NOT use multiple inheritance.
All classes MUST must inherit from a single class derived from Object
or by Object
itself.
The Object
class implements many utilities such as logging (eg. AddError
). Inheritance MUST
be public
.
Before of making a sub-class to reuse code, think if there is any way to achieve the same by composition.
DO use inline
in methods that a-priori can be called often. Good candidates are:
- accessors
- critical code called within a loop
When in doubt DO NOT inline.
DO NOT use std
libraries.
-
fstream
classes and its relatedcout
andcin
built-in streams -
regex
in the regular expression support module -
chrono
for timer and testing code
DO NOT use.
DO NOT use.
DO NOT use.
Very specific low level code (rare).
DO NOT use.
This includes the popular Boost library. All the basic and utility classes (eg. strings, containers)
are implemented in the Norm
library.
- MSMPI for Windows
- MPICH in Linux and MacOS
- Googletest for unit testing (development dependency only)
The Norm
base library provides many functionalities that are key for the Khiops development
process.
DO follow the programming by contract paradigm.
To this end, then Norm
library provides assertion macros to test the state of the program.
For example:
assert(oInstance != NULL)
will make the program exit abruptly if oInstance
is a NULL
pointer.
Norm
provides the following three macros:
-
assert
: Generic assertion to be used anywhere in the code -
require
: Asserts a method pre-condition (must be put after variable declarations) -
ensure
: Assert a method post-condition (must be put before thereturn
statement)
Note that while semantically different, the mechanism of each assertion on failure is the same: exit abruptly the program.
Do not hesitate to add assertions to make explicit hypotheses on the state of the program as they can be seen as executable comments.
Example
// Divides two positive numbers
int SomeClass::SlowDivide(int nDividend, int nDivisor) const
{
int nResult;
int nRemainder;
int i;
require(nDividend >= 0); // Assert the positivity pre-condition
require(nDivisor > 0);
// Return zero if the divisor is larger
if (nDividend < nDivisor)
{
nResult = 0;
nRemainder = nDivisor - nDividend;
}
// Try each multiple of divisor until finding the result
else
{
for (i = 1; i <= nDividend; i++)
{
assert(i * nDivisor <= nDividend); // Assert the loop invariant
if ((i+1) * nDivisor > nDividend)
{
nResult = i;
nRemainder = nDividend - (i * nDivisor);
}
}
}
ensure(nResult * nDivisor + nRemainder == nDividend); // Assert the results post-condition
return nResult;
}
The Khiops codebase has thousands of assertions, many executing the Check
method inherited from
Object
, which may contain exhaustive checks an object's state. For this reason assert macros are
available in debug builds. In release builds they are converted to empty statements to favor
execution speed.
- private vs protected
- paragraph comments
- constructeur vide