Aller au contenu

Utilisateur:Killruana/blop

Une page de Wikipédia, l'encyclopédie libre.


Ce document s’appuie sur une solide expérience de développement, de maintient et d’évolution de code sur des logiciels faisant plusieurs millions de lignes de code. Le respect des normes de programmation est la condition de viabilité à long terme du logiciel, de sa qualité à long terme et donc de la survie de l’entreprise.

Il faut presque toujours privilégier la lisibilité, la fiabilité et la cohérence du code à une rapidité d’exécution (et de programmation).

Les normes de programmation permettent d’obtenir une uniformité de l’ensemble des codes utilisés. Cela facilite la relecture mais surtout limite les bugs dès la phase de codage. De plus un bug peut être trouvé par simple lecture du code.

Lorsque l’ensemble du code évolue, certaine partie deviennent obsolètes. Il ne faut pas hésiter à réécrire un code vieillot ou à rendre un code trop complexe plus simple.

Toute fonctionnalité programmée doit être testée (tracé) dans toutes les configurations possibles par le développeur puis par une tierce personne qui valide.

Dans tous les cas c’est le bon sens qui doit prévaloir.

Généralité sur le code[modifier | modifier le code]

Le code doit être aéré (sauter des lignes de temps en temps) Il doit être aligné avec des tabulations. Si une ligne est trop longue, il est possible de la mettre sur 2 lignes.

virtual BOOL bDescriptionTableau(IDAUTO nIDTableau,
        IDAUTO nIDEnreg, IMessageXML* pclXML, BOOL bToutesColInfo);

Lorsque l’on revient sur du code, il ne faut pas hésiter à l’aérer et à ajouter des commentaires s’ils manquent.

Fichier .h, Fichier .cpp[modifier | modifier le code]

Fichier.h[modifier | modifier le code]

Tous les .h doivent commencer par

#pragma once

Ceci permet de n’inclure le .h qu’une seule fois (évite les boucles d’include)

On n’implémente pas de méthode static dans un .h ni de tableau. (sauf déclaration de variable simples et des chaînes) car sinon cela fait grossir le code compilé (parfois dans des proportions énormes).

On inclus le minimum de .h, et surtout pas stdafx.h

Fichier.cpp[modifier | modifier le code]

Le fichier commence par un commentaire qui indique a quoi il sert

Puis on inclus stdafx.h

Ensuite les includes nécessaire.

On termine TOUJOURS par l'include du .h correspondant à ce fichier .cpp

Ensuite les constantes du .cpp

Ensuite l'implémentation.

Classes[modifier | modifier le code]

Généralités[modifier | modifier le code]

Un .h et un .cpp doivent exister pour chaque classe. Le .cpp implémente les méthodes de la classe. Les classes sont préfixée par un C (ex : CMaClasse).

! Les .h et .cpp ont le nom de la classe sans le C (sinon tous les fichiers commenceraient par C et il serait plus difficile de les retrouver dans l’arborescence du projet)

On ne met pas les #include dans le .h de la classe sauf quand c'est absolument nécessaire, on utilise les prédéclaration (ex class CMaClasse; enum eMONENUM; )

Comptage des ressources[modifier | modifier le code]

Toutes les ressources allouées doivent être libérées. Dans le cas contraire, la mémoire est de plus en plus saturée au fur et à mesure de l’exécution et le logiciel fini par ne plus fonctionner.

Pour vérifier la bonne libération des ressources, nous utilisons un compteur de ressource RCOUNT qui compte chaque ressource allouée et chaque ressource désalouée.

CMaClasse  * pMonPointeur = newRC(CMaClasse, (Param1, Param2));

La macro newRC au lieu de new permet de compter la ressource allouée

deleteRC(CMaClasse, pMonPointeur);

La macro deleteRC au lieu de delete permet de compter la ressource desallouée.

Méthodes[modifier | modifier le code]

Préfixes particuliers[modifier | modifier le code]

Préfixe Description
_ méthodes protégées
__ méthodes privées
s_ méthodes statiques

A ces préfixes s'ajoute les préfixes des variables.

Exemples[modifier | modifier le code]

public:
BOOL bGetBooleen();
IInterface* piGetInterface(IDAUTO nID);
PCXSTR pszGetChaine();
static double s_dGetDouble();

protected:
double _dCalcule(int nA, int nB);
static int _s_nGetInt();

private:
BOOL __bEstCompatible();

Conditions[modifier | modifier le code]

Pour un if on préférera la forme :

// libère le message
if(m_pclMessage != NULL)
	m_pclMessage->nRelease();

A la forme

if(m_pclMessage != NULL) m_pclMessage->nRelease();

Ou à la forme (Dans ce cas là, le test doit être simple et lisible).

eRECHERCHEINFO eLocal = bLocal ? RI_LocalUniquement : RI_ReseauUniquement;

On ne fera pas

if(!m_pclMessage) m_pclMessage->nRelease();

En effet lorsque l’on trace du code pas à pas, si le if est sur plusieurs lignes on sait si l’instruction conditionnelle a été exécutée ou non.

S’il y a plusieurs conditions on les met dans plusieurs parenthèses. Ne pas hésiter à écrire sur plusieurs lignes.

if((m_piLangageTableau==NULL) && (m_eTypeAction!=eTYPEACTION_Particuliere))
{
	XASSERT(FALSE);
	return RA_Err;
}

Les { ont une ligne chacune.

//si on est connecté -> tout en réseau
if (bConnecte)
{
	CAction::eRESULTAT_ACTION eRes = RA_Ok;
	return eRes;
}
else
{
	//signale l'erreur -> on ne peut aps faire l'action
	DLLRES_bLoadString(IDS_ERR_SYNCHRO,&sMessErr);
	pclReponse->AjouteMessageEnClair(sMessErr);
	return RA_Err;
}

Dans le cas d’un booléen, on préfèrera :

if (bConnecte)

à

if (bConnecte==TRUE)

ou

if (bConnecte==1) //interdit !

Dans le cas d’un pointeur, on écrira :

if (pPointeur!=NULL)

au lieu de

if (pPointeur)

Préfixages des variables[modifier | modifier le code]

Généralités[modifier | modifier le code]

Les noms de variable et de classe doivent être si possible en français. S’il sont composés de plusieurs mots on utilisera des majuscules intermédiaire et non des _. Ex : MaVariable.

Les classes commencent par C (ex CMaClasse)
Les interfaces commencent par I (ex : IMonInterface)

Les CString sont préfixés en s (ex sNomChamp)
Les CString qui contiennent du XML doivent être préfixés en sx (ex : sxNomChaine)
Les PCXSTR sont préfixés en psz (ex : pszNomChaine) (Chaine constante )
Les PXSTR sont préfixés en sz (ex : szNomChaine) (Chaine variable, peu employée )

Les entiers sont préfixés en n (ex : nMonNombre)
Les enums sont préfixés par e (ex : eTypeVar)
Les float sont préfixés en f
Les doubles sont préfixés en d

Les IDAUTO sont préfixés en nID (Identifiant automatiques des fichiers de données)

Les pointeurs sont préfixés par p :
pnMonNombre est un pointeur sur un entier.

Les tableaux[modifier | modifier le code]

On préférera utiliser des CArray à des tableaux de tailles fixe si le nombre d’élément est variable. (On pense toujours qu’on ne va pas dépasser la limite jusqu’au jour où on la dépasse).

Une variable tableau doit toujours contenir Tab.
Un tableau d’entier peut être préfixé en nTab ;
On ne préfixe pas un tableau de pointeur par p.
Dans tous les cas donner un nom qui indique le plus clairement possible le type manipulé.

Autres[modifier | modifier le code]

Pour les autres types évolués, on pourra préfixer par le nom du type en minuscule. Par exemple :
Pour un SIZE : sizeClient
Pour un RECT : rectFenetre;

Recapitulation pour les variables[modifier | modifier le code]

Préfixe Description
p pointeur
i interface
cl classe
d double
f float
n int
nID IDAUTO
b BOOL
s CString
psz Pointeur sur chaine PCXSTR
sz Pointeur sur chaine variable PXSTR

Exemples[modifier | modifier le code]

int m_nEntier;
IDAUTO m_nIDAuto;
BOOL m_bBooleen;
double m_dDouble;
CString m_sChaine;
CPoint m_clPoint;
Interface* m_piInterface;
static int m_snNembreStatique;

Boucles, Structures, Utilisation des enums[modifier | modifier le code]

Boucles[modifier | modifier le code]

Une boucle qui enlève des éléments est une boucle inversée (un for qui fait des --) Une boucle sur un tableau dont le nombre d’élément peut changer doit être un while et pas un for.

En aucun cas on ne change les bornes ni la variable de parcours dans une boucle for.

Choisir correctement le nom de la variable de la boucle for, si vous imbriqué plusieurs boucles ne pas utiliser les variables générique i, j, etc.. mais donner un nom clair à la variable boucle. Réservé les variables a 1 lettre aux boucles simple. Si votre votre boucle commence (ou termine) sur une autre valeur que 0 pour le parcours d'un tableau indiquer pourquoi en commentaire.

Structures[modifier | modifier le code]

Les noms des structures sont préfixées par st en minuscule suivit du nom de la structure en majuscule.

	struct stACTIONIMBRIQUEE
	{
		int		nOrdre;
		IDAUTO	nIDEnreg;
		CString sxActionImb;
	};


Pour initialiser une structure on utilisera XMEMSET :

XMEMSET(&stEnregTableau, 0, sizeof(stENREG_TABLEAU));

SAUF si la structure contient des classes (CString par exemple). Sinon certaines références sont perdue, on perd des ressources ou la classe est invalide.

Les structures utilisées simplement par une classe doivent être déclarées dans la classe (en protected ou private).

Utilisation des enums[modifier | modifier le code]

Les enums sont employés lorsque l’on doit fournir une certaine valeur entière à une fonction par exemple et que cette valeur a un sens particulier.

enum ePOSFEN
{
	PF_Normal = 0,		
	PF_Maximisee = 1,			
	PF_Minimisee = 2,		
};

Les enums commencent pas e et sont tout en majuscule eNOMMAJUSCULE,ou eNOM_MAJUSCULE (Les « _ » sont autorisés entre les mots)
Exemple: eTYPEFENETRE

Valeur dans les enums : PREFIXEENUM_NomValeur
ex : TF_FenCreation

Les enums utilisés simplement par une classe doivent être déclarées dans la classe (en protected ou private). Sinon ils seront souvent déclarés en public dans la classe et devront être utilisés préfixés du nom de la classe :

CMaclasse ::eMonEnum

Chemin de répertoire, Commentaires et asserts[modifier | modifier le code]

Chemin de répertoire[modifier | modifier le code]

Lorsqu’on demande un répertoire dans une fonction ou dans une structure etc… on prend le répertoire sans le slash final (convention interne).

Il est plus facile d’ajouter un slash que de l’enlever CreateDirectory marche bien s’il n’y a pas de slash

On n'utilise jamais le répertoire courant, on ne suppose pas qu'il est correctement fixé. Les fonctions GetCurrentDirectory et SetCurrentDirectory sont à proscrites.

Commentaires et asserts[modifier | modifier le code]

Le code doit être commenté (40% de commentaires environ) en particulier les méthodes des interfaces. Le commentaire d’une méthode indique ce que fait la méthode et quelle est la nature des paramètres.

//Demande au module de préparer une action particulière
//ENTREE : l'action
//	     Le message XML à remplir
//Renvoie vrai si le message XML doit être envoyé, faux sinon
virtual BOOL bActionParticuliere(IDAUTO nIDAction, IMessageXML * piMessage) { XASSERT(FALSE); return FALSE; }


5% du code doit être du code de contrôle : des asserts.

Les asserts sont de la forme :

XASSERT(m_nNbMotclef!=0);

Si m_nNbMotclef vaut 0 alors un message d’erreur s’affiche en indiquant la ligne de l’erreur.

XASSERTC(m_nNbMotclef==0,"mot clef pas initialisé");

Dans ce cas, si m_nNbMotclef ne vaut pas 0, le message d’erreur explicite s’affiche en plus du numéro de la ligne d’erreur.

Le bon moment pour mettre un assert, c’est lorsque l’on se dit qu’une condition doit être remplie pour que le code fonctionne.

IMPORTANT : le code présent dans les ASSERT n’est compilé que dans la version débug du programme, il est donc très important de n’y exécuter que du code de vérification.

If (m_pclMessage != NULL)
	XASSERT(m_nNbMotclef!=0);
m_nNbMotclef++ ;

est un code faux car en release ce code sera équivalent à

if (m_pclMessage != NULL)
       m_nNbMotclef++ ;

on préfèrera donc

if (m_pclMessage != NULL)
{
	XASSERT(m_nNbMotclef!=0);
}
m_nNbMotclef++ ;


Les différents types d’assert

XASSERT(Condition à vérifier) ;
XASSERTC(Condition à vérifier, « Message à afficher ») ;
CHECKPTROK(pMonPointeur, sizeof(Type)) ;

XVERIFY(bFonctionQuirenvoieUnBool) ;

!! Contrairement aux autres asserts XVERIFY aura son code executé en RELEASE.

Macros et multidérivation, Norme unicode et autres types[modifier | modifier le code]

Macros et multidérivation[modifier | modifier le code]

Les macros ne doivent être utilisées que lors qu’elles deviennent indispensables. En effet, il est impossible de tracer dans une macro.

En général, il vaut mieux utiliser un static const qu’un #define. (en effet avec un static const, il y a une vérification de type à la compilation)

static const int s_nNbElem = 15;

La multidérivation est à éviter autant que possible en raison des problèmes de cast et de la VTable. On préfèrera un agrégation si c’est possible.

!!Il est conseillé de préfixer les paramètres d’une macro avec « _ » car ainsi ils ne seront pas confondus avec les paramètres de la méthode en cours.

Norme unicode et autres types[modifier | modifier le code]

Pour manipuler les chaînes et d’autres type de variables nous utilisons des normes précises :

PCXSTR pszMChaine est une chaine constante
CString sMaChaine pour une chaine qui varie
XCHAR cMonChar pour un char

L’avantage de PCXSTR et XCHAR est de rebondir sur la bonne déclaration de caractère selon que la version du logiciel est ANSI (caractère sur 1 octet) ou UNICODE (caractère sur 2 octet pour le chinois et autres langues space)

IDAUTO est un entier non signé sur 8 il est utilisé pour les identifiants uniques des objets du langage.

Ressource texte[modifier | modifier le code]

Pour faciliter la traduction du logiciel, toutes les chaînes à traduire doivent être dans les ressources de la dll. Nous utilisons un outil GESRES que nous avons développé nous-même qui permet de stocker des chaînes, de les intégrer automatiquement dans les ressources et de les traduire le moment venu.

Le code suivant permet de charger une ressource texte référencée par un identifiant dans un CString.

CString sMessErr;
DLLRES_bLoadString(IDS_ERR_SYNCHRO,&sMessErr);

Parfois une chaîne doit tout de même être représentée « en dur » dans le code. Dans ce cas on l'encadre avec la macro _X() qui se charge de transformer la chaîne en unicode s'il y a besoin.

Cas d'erreur les plus fréquents[modifier | modifier le code]

Les cas les plus fréquents d’erreur sont dût à des pointeurs.

Si un pointeur n’a pas été alloué ou déjà libéré bref s’il pointe sur une zone invalide de la mémoire, le programme plante en faisant ce que l’on appelle une GPF (Gerneral Program Failure) qui donne le petit message désagréable : « ce programme a effectué une opération non conforme… »

Il faut donc être vigilant dans la manipulation des pointeurs et ne pas hésiter à vérifier leur valeur avec des asserts... Ou a les mettre à NULL lorsque l’on a fini de s’en servir dans une fonction.