1 Plan Académique de Formation Modularisation des CAP CFA CCI Avignon 06 mars 2013.
Rappels Modularisation application C/C++
-
Upload
sylvain-leroy -
Category
Software
-
view
847 -
download
0
Transcript of Rappels Modularisation application C/C++
Modularisation Applications
C/C++Bonnes pratiques
“Parce que nous avons tous...
...rencontré ce type d’application….
...peiné à comprendre un code illi-si-ble..
...cru être maudits pour avoir à maintenir ce type d’applications
...pesé le pour et le contre avant la modification d’une ligne de code...
Développons notrearmure savoir/faire
et nos armes outils
pour chasser développer mieux.
Level max 99
Plan de la formation▧Objectifs▧Structure d’une application C/C++▧Exemples d’applications OpenSource▧Gestion des headers▧Gestion des includes▧Outillage
ObjectifsObjectifs primaires :
▧ Structurer un projet C/C++▧ Connaitre la différence entre include système et include▧ Connaître l’interaction entre le chemin des includes et les instructions
CMake▧ Savoir écrire un header C/C++ correct
Objectifs secondaires : ▧ Savoir exporter des définitions▧ Optimiser les dépendances entre les fichiers▧ Savoir pré-compiler des headers▧ Définir les bonnes pratiques de modularisation pour une application
C/C++
Que gagnons à modulariser nos applications ?
ModularisationLa programmation modulaire c’est l’art de concevoir une application logicielle qui met en oeuvre
la séparation des fonctionnalités d’un programmeen des modules indépendants, interchangeables et pour chaque module
que celui ne contienne uniquement ce qui est nécessaire à son exécution et sa fonctionnalité.
Vocabulaire
Technologie Paquetage Module
Java package *.jar
C++ namespace *.dll, *.so, *.a
C# namespace *.dll, NuGet
Comment modulariser notre application ?(Prendre un accent
La recette
1.Prendre les sources de l’application2.Récupérer les dépendances : Où ???3.Décomposer en module : Comment ??4.Construire les binaires5.Et après ? Intégration continue
Structure d’une
application C/C++
Que trouve-t-on dans une application codée en C/C+
+ ?
Bric à brac du codeur▧Scripts de compilation▧Ressources (images, fichiers de configuration)▧Documentation
ReadmeChangeLog
▧Sources▧Headers▧Bibliothèques▧...
Autopsie d’une application C/C++
Au niveau du source, un programme C/C++ est une collection de :
● Fichiers en-têtes (headers) (fichier .h ou .hpp)● Fichiers sources (fichier .c ou .cpp)
Contenu des fichiers en-têtesContenu partagé entre plusieurs fichiers sources :
▧types de données comme les structs ou les classes
▧les protypes de fonction ( déclarations)▧les constantes (les déclarations)▧les templates▧les types énumérés
La notion de dépendances entre fichiers
Tout fichier peut utiliser la directive #include afin insérer le contenu d’un autre fichierEx : Un fichier source peut inclure plusieurs fichiers en-têteEx : Un fichier en-tête peut inclure plusieurs fichiers en-tête
ExempleGame.cpp inclut Game.hGame.h inclut Item.h
Game.h et Item.h sont visibles dans Game.cpp Game.cpp
Game.h
Item.h
dépendances transitives
Dépendances circulaires
Parent.cpp
Parent.hpp Enfant.hpp
Enfant.cpp
Les fichiers Parent.hpp et Enfant.hpp dépendent l’un de l’autre
Solution pour corriger
Parent.cpp
Parent.hpp Enfant.hpp
Enfant.cpp
Les fichiers Parent.hpp et Enfant.hpp dépendent l’un de l’autre
Bien gérer ses dépendancesAccéler le temps de compilationLimiter le périmètre de régression / modificationFaciliter les futures évolutions de l’applicationAméliorer la compréhension de l’architecture global de l’application
Dépendances dans un vrai programme...
La décomposition
d’une application
C/C++
Exemples de projets C/C++
Projet NotePad++Projet
src
Composant 1
Composant 2
Fichiers .h
Fichiers .cpp
Fichiers .h
Fichiers .cpp
Fichiers .h
Fichiers .cpp
Projet WgetProjet
src
Fichiers .h
Fichiers .cpp
doc
po
Projet LibUProjet
srcs Fichiers .cpp
include
example
test
Fichiers .h
Fichiers .cpp
Fichiers .h
Fichiers .cpp
Fichiers .h
LibEvent https://github.com/libevent/libevent
Projet Fichiers .cpp
include
sample
test
Fichiers .h
Fichiers .cpp
Fichiers .h
Fichiers .cpp
Fichiers .h
Fichiers .h
Google FlagsProjet
doc
src
test
Fichiers .cpp
Fichiers .h
Fichiers .cpp
Fichiers .h
VorbisProjet
doc
lib
test
Fichiers .cpp
Fichiers .h
Fichiers .cpp
Fichiers .h
examples
Fichiers .cpp
Fichiers .h
include Fichiers .h
Structure proposée
Projet
srccpp
Fichiers .cpp
Fichiers .h
main
test
include
resources
cpp
resources
Fichiers .h
Fichiers .cpp
Fichiers .h
Structure projet classique
Mon Projet de Bibliothèque
Projet plus complexe
Bibliothèque
ExemplesExemple A
Exemple B
dépendances
Quel livrable souhaitez-vous ?
“Une archive pour la bibliothèque ?
les exemples sont dedans ou à coté ?
Une archive qui contient la bibliothèque et ses exemples-> Utilisation d’un CMake Global-> Les exemples réutilisent la bibliothèque via CMake-> La Compilation globale est réalisée via CMake
Situation A
Une archive pour la bibliothèque et une archive par exemple-> Utilisation d’un CMake pour la bibliothèque-> Utilisation d’un CMake par exemple-> Les exemples réutilisent la bibliothèque via le gestionnaire de dépendances-> La Compilation globale est réalisée via Gradle
Situation B
Digression...Quel outillage
pour le packaging ?
▧Utilisation de CMakePour la compilation multi-plateformePour l’utilisation dans Eclipse
▧Ajout de GradlePour gérer les dépendances inter-projetsPour construire le packaging et l’uploader
Solution Pragmatique
URL : https://github.com/Tocea/gradle-cpp-plugin/
Fonctionnalités de base :▧Gestionnaire de dépendances▧Construction du livrable
Fonctionnalités supplémentaires :▧Lancement Scripts de compilation▧Analyse statique de code
Rappel plugin Gradle
Travaux pratiques
Pendant ce temps...
Utilisez : ● la notion de buildType CMake● un autre sous-projet (et livrable)
Si vous avez besoin de recompiler un fichier ou une bibliothèque avec
des flags différents
Vérifiez● Que les projets ne soient pas
indépendants (release) et donc deux livrables différents
Utilisez : L’option OBJECT de CMake (http://www.cmake.org/pipermail/cmake-developers/2012-March/003693.html)
Si vous devez réutilisez des .obj entre deux sous-projets
Outillage pour gérer les dépendances
dump -H <program>
AIX
chatr <program>
HP-UX
ldd <program> Linux, SunOS5X$LIB_PATH AIX
$SHLIB_PATH
HP-UX
$LD_LIBRARY_PATH
Linux, SunOS5X
Outillage pour analyser les dépendancs
Visual StudioPC LintVisual LintEclipse IncludatorCLang Include what you use Include File DependencyJEtBrains IDE CLion
Précompilation d’entêtes (sous Unix +gcc)
stdafx.h:#include <string>#include <stdio.h>
a.cpp:#include "stdafx.h"int main(int argc, char**argv){ std::string s = "Hi"; return 0;}
Then compile as:> g++ -c stdafx.h -o stdafx.h.gch // Précompilation des headers> g++ a.cpp > ./a.out
Headers, includes et companie
Les Headers : mais pourquoi ?
http://webstory.my/
▧ Accélérer le temps de compilation
▧ Garder son code mieux organisé
▧ Séparer proprement les interfaces de l’implémentation
▧ Prérequis pour la développement de bibliothèques
▧ Compilation en deux temps :Chaque fichier est compilé avec l’ensemble de ses
headersL’ensemble des fichiers est assemblé (link) pour
construire le binaire final.▧ Les fichiers sources peuvent inclure des fichiers headers▧ Les fichiers headers peuvent inclure d’autres fichiers
headers
Comment cela fonctionne ?
Compilation et includes
Compilation et includes
// maclasse.hclass MaClasse{ void faitQuelquechose() { }};
Attention aux multiples inclusions
// main.cpp#include "maclasse.h" // définir MaClasse#include "maclasse.h" // Erreur de compilation - MaClasse already defined
#ifndef __MACLASSE_H_INCLUDED__ // si maclasse.h n’a pas encore été inclus#define __MACLASSE_H_INCLUDED__ // #define pour que le préprocesseur sache que cela a été inclus
// maclasse.hclass MaClasse{ void faireQuelquechose() { }};#endif
Attention aux multiples inclusions (solution)
▧ #include “filename”
Pour les headers maintenus par l’équipe / produit▧ #include <filename>
Pour les headers fournis par les bibliothèques standard (pré-définis)
Bibliothèques standard : (STL) + Packages installés sur la machine
▧ Sous GCC, la variable INCLUDE_PATH est exploitée pour retrouver les fichiers headers (argument -I)
Includes systèmes / includes
Les fichiers headers doivent être auto-suffisants
//=================================// include guard#ifndef __MYCLASS_H_INCLUDED__#define __MYCLASS_H_INCLUDED__
//=================================// forward declared dependenciesclass Foo;class Bar;
//=================================// included dependencies#include <vector>#include "parent.h"
//=================================// the actual classclass MyClass : public Parent // Parent object, so #include "parent.h"{public: std::vector<int> avector; // vector object, so #include <vector> Foo* foo; // Foo pointer, so forward declare Foo void Func(Bar& bar); // Bar reference, so forward declare Bar
};
#endif // __MYCLASS_H_INCLUDED__
N’incluez pas les dépendances transitives
// C.h#include “b.h”#include “a.h”
// C.cpp#include “c.h”#include “a.h” // USELESS
Positionnez l’include du source en premier
// C.cpp#include “c.hpp”
#include <iostream>...
Les fichiers headers doivent être autosuffisants.
▧ Incluez uniquement le header du module, jamais ses dépendances.
▧ Les fichiers headers ne doivent pas avoir d’instruction using::
▧ Le fichier header ne doit contenir que :Structure et déclarations de classesPrototypes de fonctionDéclaration de variables globales externes
▧ L’initialisation des variables doit être dans les fichiers sources
▧ Enlevez les déclarations internes (invisibles de l’utilisateur) et déplacez les dans les .cpp
Best practices Includes/Headers
▧ Incluez uniquement les fichiers nécessaires dans chaque header
▧ Chaque header doit avoir des gardes contre l’inclusion▧ Appliquez la règle : une structure, un fichier .h(pp) et un
fichier .c(pp)class MyClass => MyClass.hpp, MyClass.cpp
▧ Ne confondez pas #includes systèmes et #includes.▧ Utilisez les déclarations en avant (forward)▧ Attention aux fonctions inline▧ Bien gérer les déclarations externes et exportées
Best practices Includes/Headers
▧ Pas d’inclusion de fichiers .cpp▧ Déclarez les includes systèmes aussi dans les .cpp
Best practices Includes/Headers
// a.h#ifndef A_H#define A_H#include "b.h"
class A { B* b; };#endif A_H
Dépendances circulaires// b.h#ifndef B_H#define B_H#include "a.h"
class B { A* a; };#endif B_H
#include "a.h"
// start compiling a.h #include "b.h" // start compiling b.h #include "a.h" // compilation of a.h skipped because it's guarded
// resume compiling b.h class B { A* a }; // <--- ERROR, A is undeclared
Dépendances circulaires
Pour les longues nuits d’hiver▧ Modular programming : http://www.informit.com/articles/article.aspx?p=25003&seqNum=5▧ Detect Unnecessary includes (discussion) :
http://stackoverflow.com/questions/74326/how-should-i-detect-unnecessary-include-files-in-a-large-c-project
▧ Clang Include what you use : https://code.google.com/p/include-what-you-use/▧ Include File Dependency : http://www.mobile-mir.com/cpp/▧ JetBrains CLion : ▧ Include et headers : http://www.cplusplus.com/forum/articles/10627/▧ http://faculty.ycp.edu/~dhovemey/spring2011/cs320/lecture/lecture27.html