Rappels Modularisation application C/C++

Post on 22-Jan-2017

848 views 0 download

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

Vous pouvez me trouver :https://about.me/sylvain_leroy

Merci