eLynx SDK v3.3.0
C++ image processing API reference

Coding rules

Introduction

K.I.S.S.* as much as possible when writing code.

Rules are the best way to share methods and go further in project development. I fix myself rules (I try) to write clear code. The goal is to kill bad code writing side effect. This way you have more time developping than debugging. Since it doen't feel fun at first sight, it is when you have understand why you must use rules. As the project grow bigger one reach quickly at being overloaded by maintenance because of quality lack. Keep in mind that once you known and apply these rules, you just focus on development not on how the write it in C++. To have quality, just need good rules.

Have fun and happy coding.

* Keep It Simple Stupid.


Table of contents

  1. General rules

  2. Naming rules

  3. File structure

  4. Layouts

  5. Accessor implement patterns

General rules

Language

For sharing reason of sources, all source files are to be written in English.

Table of contents

Text editor setting

eLynx sdk project is designed for multi-platform and multi-programmer environment. For readability reason of the source code over many text editors, it would be safe yo have the same text formatting.

Table of contents

Primary types

Use the following primary types :

Bits unsigned integer signed integer floating point
8 eLynx::uint8 eLynx::int8 none
16 eLynx::uint16 eLynx::int16 none
32 eLynx::uint32 eLynx::int32 float
64 eLynx::uint64 eLynx::int64 double

You must include definition file CoreTypes.h to access these types.

Table of contents

Naming rules

All the code MUST be written in English.

Type naming

TODO

Table of contents | Naming rules

Class naming

TODO

Table of contents | Naming rules

Method naming

Methods of functions names MUST begin by a verb (action), and be writen in mixed case starting with upper case. Execption for event handler that begins with 'On'

Purpose

Naming

Example

Data access SetData(...) et GetData(...) see accessor
returning aggregation creation CreateObject(...) Cube * CreateCube(...)
destroy agregated object created with CreateObject Remove(...) Remove(Cube *)
building agregated object BuildObject(...) BuildCube(...)
Action on an objet 'Action'Object(...) MoveTriangle(...)
Event handler OnEvent(...) OnComboChange(...)
return of boolean state IsState(...) IsSelected(...)
return of boolean attribut HasAttribute(...) HasTexture(...)
return pre-conditional state for an action CanAction(...) CanSave(...)
todo Compute todo
todo Update

todo

Not to make of amalgam between GetObject which recovers a reference, CreateObject which recovers an aggregation from external method and BuildObject which recovers an aggregation from internal method. A frequent error is to turn over an aggregation with the Get method. That can generate an memory leak. However, there is an exception to this principle, if Get creates the object in a garbage collector.

todo
Table of contents | Naming rules

Macro naming

Macro are written in uppercase using underscore to separate word and prefixing by elx.

elxASSERT();
elxSAFE_DELETE(pnObject);
Table of contents | Naming rules

Enum naming

TODO

Table of contents | Naming rules

Constant naming

TODO

Table of contents | Naming rules

Variables naming

A variable name is composed by tree informations : [Visibility]{Type}[Meaning]

Visibility

Visibility is a required information that grealty help avoiding maning side effects. This point allows to save long debugging time for stupid errors.

Prefix

Meaning

(nothing) method or function local variable
_ (underscore) class or struct data member
_s (static) static class or struct data member
i (input) input only method/function parameter
o (output) output only method/function parameter
io (input and output) input and output method/function parameter
g_ (global) global variable (banned)
the_ (the only one) singleton object (specific global)
s_ (static) module static variable
ms_ (method's static) method or function static variable

In resume :

Prefix

Meaning

_? class/struct data member (normal _, or static _s)
?_ data out of a class/struct (global g_, local s_, singleton the_, method/function static ms_)
i,o,io method or function parameter
(nothing) method or function local variable

Type

This information is required for the pointers type in order to control the pointed object life-cycle. It is optional for the other types of data. I recommand to use it for boolean and counter variables. You can define your own types convention but don't use type name beginning with i,o or s to respect the visibility rules.

Meaning

The meaning is in mixed case starting with upper case.

Ex: in file MyClass.h

extern uint g_Value;     // global variable BANNED

class MyClass
{
public:
   MyClass();
   void SetValue(uint iValue);
   uint GetValue() const;

   void DoSomething();

protected:
    uint _Value;
    static uint _snRef;   // class static as class instance reference counter
};

in file MyClass.cpp

#include "MyClass.h"

MyClass the_ClassInstance;          // singleton

uint g_Value = 1;                   // global variable BANNED
static uint s_nCall = 0;            // module variable
uint MyClass::_snRef = 0;           // class static variable

MyClass::MyClass() : _Value(0)
{
  _snRef++;                         // class static

} // default constructor

void MyClass::SetValue(uint iValue) // input parameter
{
    _Value = iValue;      // class member equals input parameter

} // SetValue

uint MyClass::GetValue() const
{
    return _Value;        // class member

} // GetValue

void MyClass::DoSomething()
{
    static bool ms_bFirst = true;       // method static variable
    if (ms_bFirst)
    {
       ms_bFirst = false;
       ...
    }
    s_nCall++;           // module static

} // DoSomething
Table of contents | Naming rules

Pointer naming

Why define a rule of nomination of pointer ?

The use of the pointers of the c++ causes great confusion. The c++ not being enough precise to completely describe the implication of a pointer, the only way of avoiding these confusions is to set nomination rules. The limitations of the c++ rise from the lack of information on the one hand on the structure and on the other hand on the cycle of life of the data carried by a pointer.

In order to manage well the pointed object life cycle it is fundamental to name them with precision. There are two great types of pointer:

Smart pointer are not taken into account ...

Reference pointer

A pointer is said by reference, when its user (customer) manages neither construction, nor destruction of the pointed object. Referencing is a relative characteristic seen of an object or a method, i.e. that a aggregated pointer incorporated by an object, is seen referenced by another object or another method. A referenced pointer is prefixed by pr as pointer on reference.

int * CreateList()
{
  int * plArray = new int [10];   // the owner is the method
  int * <b>pr</b>Item = plArray;  // just a reference
  for (int i=0; i&lt;10; i++)
    *<b>pr</b>Item++ = i;

  return plArray;          // method transfert aggregation to the client
    
} // CreateList

Aggregation pointer

A pointer is said by aggregation, when its user (client) deals either on construction, or on the destruction of the pointed object. The dispersion of the responsibilities for the cycle of life of an object is harmful. Most of the time it is the proof of a bad design. Dispersion i.e. when an object is in responsibility of build and that another is in responsibility of destroy, very often generates memory leaks or crash.

As much as possible respect this rule: that which creates is that which removes.

TODO common bad design sample

pointer prefix

construction

destruction

pn (new/delete) pnObject = new Object; delete pnObject; pnObject = NULL;
pl (liste new[]/delete[]) plObject = new Object [n]; delete [] plObject; plObject = NULL;
pm (malloc/free) pmObject = (Object*)::malloc(sizeof(Object)); if (NULL != pmObject){::free(pmObject); pmObject = NULL;}
pf (object factory method) pfObject = Factory.Create(..) Factory.Remove(pf Object);
ps (object self destroy) psObject = ? creation method if (NULL != psObject) { psObject->Release(); ps Object= NULL;}
pa (unlistedaggregation) paObject = ? creation method unlist destruction method; paObject = NULL;
pg (garbage collector) pg Object = ? creation method pgObject = NULL; at end of program call to a garbage

Garbage collector pg: one creates an object with a factory and this factory deals with the destruction of this object.

None defined aggregation pa: when one is with an aggregation not in cases pn, pl, pm, ps, pf and pg. You known that you have allocate a ressource and that you have to de-allocate it with the corresponding function. For example :

// construction
FILE * paFile = ::fopen(...);
...  
// destruction
::fclose(paFile);
paFile = NULL;

Mixed/Multiple pointer

This type of pointer is to be proscribed, but as in very good software, it forms part of a heavy heritage of approximate design. It is used to define pointers used with multiple modes of construction/destruction. One associates a variable then to him defining the good manners to destroy the object. A mixed pointer is prefixed by pM (Mixtes, Multiple). Example :

TODO

Composed pointers

TODO

Table of contents | Naming rules

File structure

Definition file (.h)

The header filename is the same as the class name. It must have the .h extension. Only declare one class per file.

Ex : file MyClass.h define the class MyClass.

Header

//----------------------------------------------------------------------------
//  MyClass.h                                          [lib].Component package
//----------------------------------------------------------------------------
//  Usage : [Description]
//----------------------------------------------------------------------------
//  Inheritance :
//    MyClass
//      +BaseClass
//----------------------------------------------------------------------------
//  Copyright (C) [this year] by eLynx project
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Library General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//  See the GNU Library General Public License for more details.
//----------------------------------------------------------------------------

Body

#ifndef __MyClass_h__
#define __MyClass_h__

#include "[lib]Lib.h"
#include "BaseClass.h"

... 

namespace eLynx {

class ExportedBy[lib] MyClass : public BaseClass
{
...
};

} // namespace eLynx

#include "inl/MyClass.inl"

#endif // __MyClass_h__
Table of contents

Inline implementation file (.inl)

The inline filename is the same as the class name. It must have the .inl extension. Only declare one file per class.

Ex : file MyClass.inl implement inline methods of the class MyClass.

Header

//----------------------------------------------------------------------------
//  MyClass.inl                                        [lib].Component package
//----------------------------------------------------------------------------
//  Copyright (C) [this year] by eLynx project
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Library General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//  See the GNU Library General Public License for more details.
//----------------------------------------------------------------------------

Body

// Include statement
#include "SomeClass.h"
... 

namespace eLynx {

//----------------------------------------------------------------------------
inline returntype MyClass::Method1(...)
{
  ...
} // MyClass::Method1

//----------------------------------------------------------------------------
inline returntype MyClass::Method2(...)
{
  ...
} // MyClass::Method2

...

} // namespace eLynx
Table of contents

Implementation file (.cpp)

The implementation filename is the same as the class name. It must have the .cpp extension.

Ex : file MyClass.cpp implement the class MyClass.

Header

//----------------------------------------------------------------------------
//  MyClass.cpp                                        [lib].Component package
//----------------------------------------------------------------------------
//  Usage :  Class to represent my class
//----------------------------------------------------------------------------
//  Copyright (C) [this year] by eLynx project
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Library General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//  See the GNU Library General Public License for more details.
//----------------------------------------------------------------------------

Body

#include "MyClass.h"
...

namespace eLynx {

...

} // namespace eLynx

Component library definition file

//============================================================================
//  [MyLib]Lib.h                                     [MyLib].Component package
//============================================================================
//  Usage : declare [MyLib].Component access from dll.
//----------------------------------------------------------------------------
//  Copyright (C) [this year] by eLynx project
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Library General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//  See the GNU Library General Public License for more details.
//----------------------------------------------------------------------------
#ifndef __[MyLib]Lib_h__
#define __[MyLib]Lib_h__

// for windows platform
#ifdef elxWINDOWS
# ifdef ELYNX_[MYLIB]_EXPORTS
#   define ExportedBy[MyLib] __declspec(dllexport)   // declaration in dll library.
# else
#   define ExportedBy[MyLib] __declspec(dllimport)   // client of the dll library.
# endif
#else
# define ExportedBy[MyLib]                           // client of the static library.
#endif

#endif // __[MyLib]Lib_h__
Table of contents

Component package definition file

//============================================================================
//  [MyLib]Package.h                                 [MyLib].Component package
//============================================================================
//  Usage : package of [MyLib].Component definition
//----------------------------------------------------------------------------
//  Copyright (C) [this year] by eLynx project
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Library General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//  See the GNU Library General Public License for more details.
//----------------------------------------------------------------------------
#ifndef __[MyLib]Package_h__
#define __[MyLib]Package_h__

#include "[MyLib]Types.h"
#include "[MyLib]Errors.h"
#include "[MyLib]Macros.h"

#include "Something.h"
...

#endif // __[MyLib]Package_h__
Table of contents

Layouts

Method layout

It is important to document the methods in the .h well and to take again this information to deepen them in the cpp. That can appear tiresome with the first access, but, the fact of duplicating information, makes it possible to better become aware of the relevance of the prototyping of the method. This stage is of primary importance for a new development. Indeed, it will emphasize all the weaknesses of precipitation. One forces oneself to think twice. The first time at the time of prototyping in the .h, the second at the time of the implementation in the .cpp. That makes it possible to check the control of the following points:


Method layout pattern

//----------------------------------------------------------------------------
//  [Method name] :  [Description]
//----------------------------------------------------------------------------
//  [Visibility]
//============================================================================
//  [Object life-cycle management]
//============================================================================
//  In  : {Stack parameter list}
//        ...
//  Out : {Return}
//----------------------------------------------------------------------------
returntype MyClass::[Method name]({Stack parameter list}) {Constness}
{
  ...
  [Object life-cycle management]
  ...
  return returntype; {Return}
} // [Method name]

Here is a methodology to fill all the differents fields of the method header. Follow the ordered check list of questions to ask yourself to check the quality of both prototype and implementation.

Table of contents | Method layout

[Method name]

This is the name of the method to implement.

Table of contents | Method layout

[Description]

Here is functionnality description of the method. Add the limitations, the algorithm description ... all that a caller must be informed of. You don't need to fill the description if it is redundant with its name or implicit.

//----------------------------------------------------------------------------
//  MyMethod Q1 Q2 : allows to ...
//----------------------------------------------------------------------------
returntype MyClass::MyMethod Q1 Q2 (...)
{
} // MyMethod Q1 Q2
Table of contents | Method layout

[Constness]

//----------------------------------------------------------------------------
//  MyMethod : allows to ...
//----------------------------------------------------------------------------
returntype MyClass::MyMethod (...) const Q3
{
 ...
} // MyMethod

//----------------------------------------------------------------------------
//  MyMethod2 : allows to ...
//----------------------------------------------------------------------------
returntype MyClass::MyMethod2 (...) // not const Q3
{
 ...
} // MyMethod2
Table of contents | Method layout

[Visibility]

One indicates the visibility of the method by picking again information of the header.

For a class method ask yourself :

For a module function ask yourself :

If a class method is virtual, to indicate the name of the class from where virtuality appears. So check, in the file .h, that the declaration of the method has the keyword virtual and a comment // virtual from ... . If this information is not present, add it. This helps understanding, the role of the class in the software architecture.

Example in MyClass.h

class MyClass : public OtherClass
{
    ...
public:
    // virtual from BaseClass Q5
    virtual returntype MyMethod (...); Q4 Q6
    ...

protected:
    // virtual from OtherClass Q5
    virtual returntype OtherMethod (...); Q4 Q6
    ...
};

In MyClass.cpp

//----------------------------------------------------------------------------
//  MyMethod: allows to ...
//----------------------------------------------------------------------------
//  public virtual from BaseClass Q4 Q5 Q6 Q7
//----------------------------------------------------------------------------
returntype MyClass::MyMethod (...)
{
 ...
} // MyMethod

//----------------------------------------------------------------------------
//  OtherMethod : allows to ...
//----------------------------------------------------------------------------
//  protected virtual from OtherClass Q4 Q5 Q6 Q7
//----------------------------------------------------------------------------
returntype MyClass::OtherMethod (...)
{
 ...
} // OtherMethod
Table of contents | Method layout

[Object life-cycle management]

One will frame this information with two lines of characters '='. That allows to a client of the method to clearly and quickly identify his responsibility with respect to the life-cycle of the managed objects used by the method. This is the contract between client and method in 'black-box' mode. That is the method witch fix the contract and the client that accepts it. When this kind of information is missing every method's client coder must looks at the method code to understand there responsability. In most cases they don't care about and gererated leaks without knowing (but "it's working"). Soon or later memory problems come to remember them they are bad coder. The coder to blame is the one who don't expose the contract. This rules is for saving your client wasting time !

//----------------------------------------------------------------------------
//  CreateMyObject : allows to ...
//----------------------------------------------------------------------------
//  public virtual from BaseClass
//============================================================================
//  caller is responsible to delete the return pointer when no longer used Q8
//============================================================================
MyObject * MyClass::CreateMyObject()
{
    MyObject * pnMyObject = new MyObject();
    return pnMyObject; Q8

} // CreateMyObject
Table of contents | Method layout

{Stack parameter list}

If method has no parameter, use the MyMethod() notation. Don't use MyMethod(void).

For each parameter in the list ask yourself :

Table of contents | Method layout

[Method return]

Describe here that kind of value the method returns. To specify the various possible values of return and their significances. If it returns a pointer on aggregation, to check [Object life-cycle management].

Table of contents | Method layout

Ex: a full method header :

//----------------------------------------------------------------------------
//  CanAttach : checks if we can attach a other MyClass to this
//----------------------------------------------------------------------------
//  public
//----------------------------------------------------------------------------
//  In  : const MyClass& iOther : other object to check attachment with
//  Out : bool : true if iOther can be attached, false otherwise
//----------------------------------------------------------------------------
bool MyClass::CanAttach(const MyClass& iOther) const
{
 ...
} // CanAttach
Table of contents | Method layout

Accessor implement patterns

One uses different patterns according to the variable type. If the class variable is static, methods accessor are also static. Take care of removing method constness in the following patterns.

Table of contents | Accessor implement patterns

Primary types (char, ubyte, byte, ushort, short, uint, int, float, ularge, large, double)

uint _Value;

void SetValue(uint iValue)         { _Value = iValue; }
uint GetValue() const              { return _Value;   }
void GetValue(uint& oValue) const  { oValue = _Value; }
Table of contents | Accessor implement patterns

Boolean type bool

TODO
Table of contents | Accessor implement patterns

Compacted boolean type (U32,U64)

TODO
Table of contents | Accessor implement patterns

Object type (class, struct)

MyObject _Object;

void SetObject(const MyObject& iObject) { _Object = iObject; }

// retrieves an object reference
MyObject& GetObject()             { return _Object; }
const MyObject& GetObject() const { return _Object; }

// retrieves an object copy (call the object copy constructor)
void GetObject(MyObject& oObject) const { oObject = _Object; }
MyObject GetObject() const { return _Object; } // FORBIDDEN always return object reference
For performance reasons, always implement Get returning the object reference (&).
Table of contents | Accessor implement patterns

(c) Christophe Poudras


Generated on Thu Dec 9 2010 by doxygen 1.7.2