IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Directives de préprocesseur


Sujet :

Langage C++

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Lycéen
    Inscrit en
    avril 2013
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Morbihan (Bretagne)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : avril 2013
    Messages : 11
    Points : 9
    Points
    9
    Par défaut Directives de préprocesseur
    Bonjour.
    Je crée un petit programme avec la SFML.
    Le programme marche mais désormais je veux séparer les fonctions du fichier main.cpp dans des fichiers Fonctions.h et Fonctions.cpp, et là le compilateur me dit 'Multiple definition of'.
    D'après ce que je trouve sur internet ce serait un problème de Directives de préprocesseur qui doit déclarer plusieurs fois ma variable à cause de mes #include.
    Effectivement je déclare dans le 'main.cpp' et dans 'Fonctions.cpp' le même #include "Fonctions.h" ce qui doit être la source de mes ennuis car le compilateur doit comprendre que je déclare deux fois mes variables.
    Auriez vous une solution.

    main.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    #include <SFML/Window.hpp>
    #include <SFML/Graphics.hpp>
     
    #include "Bouton.h"
    #include "Recepteur.h"
    #include "Objet.h"
    #include "Fonctions.h"
     
    #include <iostream>
     
    using namespace std;
     
    bool debutProg = true;
     
    int main()
    {
     
        sf::VideoMode videoMode(400, 300);
        sf::RenderWindow window(videoMode, "Fenetre SFML");
     
        while (window.isOpen())
        {
            sf::Event event;
     
            Bouton bouton1(100, 50, 10);
            Bouton bouton2(180, 50, 10);
     
            if (debutProg)
            {
                bouton1.setSelect(select);
                debutProg = false;
            }
     
            Recepteur recept1(50,100,50,50);
     
            while (window.pollEvent(event))
            {
                if (event.type == sf::Event::EventType::Closed)
                    window.close();
            }
     
            window.clear();
     
            gestionBoutons(window, bouton1, bouton2, recept1);
     
            bouton1.miseAjour();
            bouton2.miseAjour();
     
            bouton1.afficher(window);
            bouton2.afficher(window);
     
            recept1.afficher(window);
     
            window.display();
        }
     
        return 0;
    }
    Objet.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #ifndef OBJET_H
    #define OBJET_H
     
    #include <SFML/Graphics.hpp>
     
    class Bouton;
    class Recepteur;
     
    class Objet
    {
        public:
            Objet();
     
        protected:
            int Xpos;
            int Ypos;
            sf::Color couleur;
     
        private:
     
    };
     
    #endif // OBJET_H
    Bouton.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    #ifndef BOUTON_H
    #define BOUTON_H
     
    #include <SFML/Graphics.hpp>
    #include <SFML/Window.hpp>
     
    #include "Objet.h"
    #include "Recepteur.h"
     
     
    class Bouton : public Objet
    {
    public:
        Bouton();
        Bouton(int posX, int posY, int ray);
        void afficher(sf::RenderTarget &target);
        void miseAjour();
        bool estDansCercle(sf::Vector2i &pos);
        bool getSelect();
        void setSelect(bool &etat);
     
    private:
        sf::CircleShape cercle;
        int rayon;
        bool selectionne;
    };
     
    #endif // BOUTON_H
    Bouton.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    #include "Bouton.h"
     
    #include <SFML/Graphics.hpp>
    #include <SFML/Window.hpp>
     
    #include <iostream>
    #include <math.h>
     
    using namespace std;
     
     
     
    Bouton::Bouton()
    {
     
    }
     
    Bouton::Bouton(int posX, int posY, int ray)
    {
            couleur = sf::Color::White;
            rayon = ray;
            Xpos = posX;
            Ypos = posY;
            cercle.setRadius(rayon);
            cercle.setPosition(Xpos,Ypos);
            cercle.setFillColor(couleur);
    }
     
    void Bouton::afficher(sf::RenderTarget &target)
    {
        target.draw(cercle);
    }
     
    void Bouton::miseAjour()
    {
        if (selectionne) cercle.setFillColor(sf::Color::Green);
        else cercle.setFillColor(sf::Color::White);
    }
     
    bool Bouton::estDansCercle(sf::Vector2i &pos)
    {
        sf::Vector2f posCercle = cercle.getPosition();
     
        sf::Vector2f centreCercle((posCercle.x + (cercle.getRadius())),(posCercle.y + (cercle.getRadius())));
     
        int XdeplOrig = pos.x-centreCercle.x;
        int YdeplOrig = pos.y-centreCercle.y;
     
        int distOrigFenetre = sqrt((XdeplOrig*XdeplOrig)+(YdeplOrig*YdeplOrig));
     
        return distOrigFenetre<cercle.getRadius();
    }
     
    bool Bouton::getSelect()
    {
        return selectionne;
    }
     
    void Bouton::setSelect(bool &etat)
    {
        selectionne=etat;
    }
    Recepteur.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #ifndef RECEPTEUR_H
    #define RECEPTEUR_H
     
    #include <SFML/Graphics.hpp>
     
    #include "Objet.h"
     
    class Recepteur : public Objet
    {
        public:
            Recepteur();
            Recepteur(int posX, int posY, int larg, int haut);
            void afficher(sf::RenderTarget &target);
            void setCouleur(sf::Color coul);
     
        private:
            sf::RectangleShape rectangle;
            int largeur;
            int hauteur;
    };
     
    #endif // RECEPTEUR_H
    Recepteur.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    #include "Recepteur.h"
     
    #include <iostream>
     
    using namespace std;
     
    Recepteur::Recepteur()
    {
     
    }
     
    Recepteur::Recepteur(int posX, int posY, int larg, int haut)
    {
        Xpos = posX;
        Ypos = posY;
        largeur = larg;
        hauteur = haut;
        couleur = sf::Color::Blue;
        rectangle.setPosition(sf::Vector2f(Xpos, Ypos));
        rectangle.setSize(sf::Vector2f(largeur, hauteur));
    }
     
    void Recepteur::afficher(sf::RenderTarget &target)
    {
        rectangle.setFillColor(couleur);
        target.draw(rectangle);
    }
     
    void Recepteur::setCouleur(sf::Color coul)
    {
        couleur = coul;
    }
    Fonctions.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #ifndef FONCTIONS_H_INCLUDED
    #define FONCTIONS_H_INCLUDED
     
    #include <SFML/Graphics.hpp>
    #include <SFML/Window.hpp>
     
    #include "Bouton.h"
    #include "Recepteur.h"
     
    bool lastClick = false;
    bool select = true;
     
    void gestionBoutons(const sf::Window &target, Bouton &bout1, Bouton &bout2, Recepteur &rec1);
     
    #endif // FONCTIONS_H_INCLUDED
    Fonctions.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    #include "Fonctions.h"
     
    #include <iostream>
     
    using namespace std;
     
     
    void gestionBoutons(const sf::Window &target, Bouton &bout1, Bouton &bout2, Recepteur &rec1)
    {
        sf::Vector2i position = sf::Mouse::getPosition(target);
     
        bool click = (bout1.estDansCercle(position) || bout2.estDansCercle(position)) && sf::Mouse::isButtonPressed(sf::Mouse::Left);
     
        if (click != lastClick)
        {
            if (click)
            {
                cout << "click" << endl;
                if (bout1.estDansCercle(position))
                {
                    select = true;
                    bout1.setSelect(select);
                    select = false;
                    bout2.setSelect(select);
                }
                else if (bout2.estDansCercle(position))
                {
                    select = false;
                    bout1.setSelect(select);
                    select = true;
                    bout2.setSelect(select);
                }
            }
        }
        lastClick = click;
     
        if (bout1.getSelect())rec1.setCouleur(sf::Color::Magenta);
        if (bout2.getSelect())rec1.setCouleur(sf::Color::Yellow);
    }

  2. #2
    Responsable Systèmes


    Homme Profil pro
    Technicien maintenance
    Inscrit en
    août 2011
    Messages
    14 208
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Technicien maintenance
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : août 2011
    Messages : 14 208
    Points : 32 631
    Points
    32 631
    Par défaut
    Tu dois utiliser les include guard dans tes fichiers .h.

    exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    #ifndef nom_fichier_include_H
    #define nom_fichier_include_H
     
    // contenu du fichier include
     
    #endif
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur la création d'un système : http://chrtophe.developpez.com/tutoriels/minisysteme/
    Mon article sur le P2V : http://chrtophe.developpez.com/tutoriels/p2v/
    Consultez nos FAQ : Windows, Linux, Virtualisation

  3. #3
    Futur Membre du Club
    Homme Profil pro
    Lycéen
    Inscrit en
    avril 2013
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Morbihan (Bretagne)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : avril 2013
    Messages : 11
    Points : 9
    Points
    9
    Par défaut
    Non, j'ai bien des include guards dans mon fichier 'fonctions.h'. Ca doit être autre chose.
    Dans 'main.cpp' et dans 'fonctions.cpp' j'ai '#include "fonctions.h"' et dans 'fonctions.h' j'ai bien des include guards.
    Pour être plus clair, j'ai mis des commentaires dans le code.

    main.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    #include <SFML/Window.hpp>
    #include <SFML/Graphics.hpp>
     
    #include "Bouton.h"
    #include "Recepteur.h"
    #include "Objet.h"
    #include "Fonctions.h" // <-- Premiere inclusion de "Fonctions.h"
     
    #include <iostream>
     
    using namespace std;
     
    bool debutProg = true;
     
    int main()
    {
     
        sf::VideoMode videoMode(400, 300);
        sf::RenderWindow window(videoMode, "Fenetre SFML");
     
        while (window.isOpen())
        {
            sf::Event event;
     
            Bouton bouton1(100, 50, 10);
            Bouton bouton2(180, 50, 10);
     
            if (debutProg)
            {
                bouton1.setSelect(select);
                debutProg = false;
            }
     
            Recepteur recept1(50,100,50,50);
     
            while (window.pollEvent(event))
            {
                if (event.type == sf::Event::EventType::Closed)
                    window.close();
            }
     
            window.clear();
     
            gestionBoutons(window, bouton1, bouton2, recept1);
     
            bouton1.miseAjour();
            bouton2.miseAjour();
     
            bouton1.afficher(window);
            bouton2.afficher(window);
     
            recept1.afficher(window);
     
            window.display();
        }
     
        return 0;
    }
    Fonctions.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
     
    #include "Fonctions.h"   // <-- Deuxieme inclusion de "Fonctions.h"
     
    #include <iostream>
     
    using namespace std;
     
     
    void gestionBoutons(const sf::Window &target, Bouton &bout1, Bouton &bout2, Recepteur &rec1)
    {
        sf::Vector2i position = sf::Mouse::getPosition(target);
     
        bool click = (bout1.estDansCercle(position) || bout2.estDansCercle(position)) && sf::Mouse::isButtonPressed(sf::Mouse::Left);
     
        if (click != lastClick)
        {
            if (click)
            {
                cout << "click" << endl;
                if (bout1.estDansCercle(position))
                {
                    select = true;
                    bout1.setSelect(select);
                    select = false;
                    bout2.setSelect(select);
                }
                else if (bout2.estDansCercle(position))
                {
                    select = false;
                    bout1.setSelect(select);
                    select = true;
                    bout2.setSelect(select);
                }
            }
        }
        lastClick = click;
     
        if (bout1.getSelect())rec1.setCouleur(sf::Color::Magenta);
        if (bout2.getSelect())rec1.setCouleur(sf::Color::Yellow);
    }
    Fonctions.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #ifndef FONCTIONS_H     // <-- Les include guards sont bien là
    #define FONCTIONS_H     // <-- Les include guards sont bien là
     
    #include <SFML/Graphics.hpp>
    #include <SFML/Window.hpp>
     
    #include "Bouton.h"
    #include "Recepteur.h"
     
    bool lastClick = false; // <-- Le compilateur me dit : multiple definition of 'lastClick'
    bool select = true;
     
    void gestionBoutons(const sf::Window &target, Bouton &bout1, Bouton &bout2, Recepteur &rec1);
     
    #endif // FONCTIONS_H_INCLUDED
    Reste du code si besoin:
    Objet.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #ifndef OBJET_H
    #define OBJET_H
     
    #include <SFML/Graphics.hpp>
     
    class Bouton;
    class Recepteur;
     
    class Objet
    {
        public:
            Objet();
     
        protected:
            int Xpos;
            int Ypos;
            sf::Color couleur;
     
        private:
     
    };
     
    #endif // OBJET_H
    Objet.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #include "Objet.h"
    #include "Bouton.h"
     
    Objet::Objet()
    {
     
    }
    Bouton.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    #ifndef BOUTON_H
    #define BOUTON_H
     
    #include <SFML/Graphics.hpp>
    #include <SFML/Window.hpp>
     
    #include "Objet.h"
    #include "Recepteur.h"
     
     
    class Bouton : public Objet
    {
    public:
        Bouton();
        Bouton(int posX, int posY, int ray);
        void afficher(sf::RenderTarget &target);
        void miseAjour();
        bool estDansCercle(sf::Vector2i &pos);
        bool getSelect();
        void setSelect(bool &etat);
     
    private:
        sf::CircleShape cercle;
        int rayon;
        bool selectionne;
    };
     
    #endif // BOUTON_H
    Bouton.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    #include "Bouton.h"
     
    #include <SFML/Graphics.hpp>
    #include <SFML/Window.hpp>
     
    #include <iostream>
    #include <math.h>
     
    using namespace std;
     
     
     
    Bouton::Bouton()
    {
     
    }
     
    Bouton::Bouton(int posX, int posY, int ray)
    {
            couleur = sf::Color::White;
            rayon = ray;
            Xpos = posX;
            Ypos = posY;
            cercle.setRadius(rayon);
            cercle.setPosition(Xpos,Ypos);
            cercle.setFillColor(couleur);
    }
     
    void Bouton::afficher(sf::RenderTarget &target)
    {
        target.draw(cercle);
    }
     
    void Bouton::miseAjour()
    {
        if (selectionne) cercle.setFillColor(sf::Color::Green);
        else cercle.setFillColor(sf::Color::White);
    }
     
    bool Bouton::estDansCercle(sf::Vector2i &pos)
    {
        sf::Vector2f posCercle = cercle.getPosition();
     
        sf::Vector2f centreCercle((posCercle.x + (cercle.getRadius())),(posCercle.y + (cercle.getRadius())));
     
        int XdeplOrig = pos.x-centreCercle.x;
        int YdeplOrig = pos.y-centreCercle.y;
     
        int distOrigFenetre = sqrt((XdeplOrig*XdeplOrig)+(YdeplOrig*YdeplOrig));
     
        return distOrigFenetre<cercle.getRadius();
    }
     
    bool Bouton::getSelect()
    {
        return selectionne;
    }
     
    void Bouton::setSelect(bool &etat)
    {
        selectionne=etat;
    }
    Recepteur.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #ifndef RECEPTEUR_H
    #define RECEPTEUR_H
     
    #include <SFML/Graphics.hpp>
     
    #include "Objet.h"
     
    class Recepteur : public Objet
    {
        public:
            Recepteur();
            Recepteur(int posX, int posY, int larg, int haut);
            void afficher(sf::RenderTarget &target);
            void setCouleur(sf::Color coul);
     
        private:
            sf::RectangleShape rectangle;
            int largeur;
            int hauteur;
    };
     
    #endif // RECEPTEUR_H
    Recepteur.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    #include "Recepteur.h"
     
    #include <iostream>
     
    using namespace std;
     
    Recepteur::Recepteur()
    {
     
    }
     
    Recepteur::Recepteur(int posX, int posY, int larg, int haut)
    {
        Xpos = posX;
        Ypos = posY;
        largeur = larg;
        hauteur = haut;
        couleur = sf::Color::Blue;
        rectangle.setPosition(sf::Vector2f(Xpos, Ypos));
        rectangle.setSize(sf::Vector2f(largeur, hauteur));
    }
     
    void Recepteur::afficher(sf::RenderTarget &target)
    {
        rectangle.setFillColor(couleur);
        target.draw(rectangle);
    }
     
    void Recepteur::setCouleur(sf::Color coul)
    {
        couleur = coul;
    }

  4. #4
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    juin 2011
    Messages
    695
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : juin 2011
    Messages : 695
    Points : 3 356
    Points
    3 356
    Par défaut
    Une variable globale (non const) est une très mauvaise idée, encore plus dans un .h où tout le monde y a accès.

    Ton problème est double:

    - les 2 variables globales sont un détail d'implémentation de la fonction gestionBoutons(): elles n'ont rien à faire dans le .h
    - les 2 variables globales sont présentes plusieurs fois car non extern. Mais elles ne devraient de toute manière pas exister.

    Une solution est de les mettre dans le .cpp de gestionBoutons(). Une meilleure solution est de ne pas avoir un état global et de faire faire une classe pour la gestion des boutons ou de mettre explicitement les états en paramètre et retourner le nouvel état.

  5. #5
    Membre confirmé
    Femme Profil pro
    ..
    Inscrit en
    décembre 2019
    Messages
    242
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 91
    Localisation : Autre

    Informations professionnelles :
    Activité : ..

    Informations forums :
    Inscription : décembre 2019
    Messages : 242
    Points : 533
    Points
    533
    Par défaut
    Salut,

    bool lastClick = false; // <-- Le compilateur me dit : multiple definition of 'lastClick'
    dans ton fichier.h partagé, tu mets la déclaration extern bool myvar; et dans fichier.cpp tu y places la définition bool myvar= true; Conf ODR: One Definition Rule , ceci juste pour pointer le problème.

  6. #6
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    octobre 2004
    Messages
    11 472
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : octobre 2004
    Messages : 11 472
    Points : 29 726
    Points
    29 726
    Par défaut
    Salut,

    Il faut déjà comprendre comment fonctionne la chaine de compilation pour arriver à comprendre ton problème.

    Car, pour générer un exécutable (pour compiler ton programme, va-t-on dire), on va en réalité utiliser trois outils distincts:
    1. le préprocesseur
    2. le compilateur proprement dit
    3. l'éditeur de liens


    1- Le préprocesseur
    Le préprocesseur est un outil vraiment basique qui peut créer certains "symboles" (à l'aide de la directive #define) et en tester l'existence (entre autre à l'aide des directives #ifdef ou [c]#ifndef[]) afin de décider ce qui doit être gardé pour la suite.

    C'est la raison pour laquelle le "squelette" d'un fichier d'en-tête va prendre la forme générale de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    #ifndef UN_SYMBOLE_QUELCONQUE // si UN_SYMBOLE_QUELCONQUE n'existe pas encore, alors on exécute ce qui suit
    #define UN_SYMBOLE_QUELCONQUE // on commence par définir le symbole en question
    /* le contenu du fichier d'en-tête
    #endif  // fin de la condition #ifndef
    De pllus, le préprocesseur va "tout simplement" remplacer (de manière récursive) les directives #include <un_nom_de_fichier> (ou #include "un_nom_du_fichier") par ... le contenu du fichier en question et supprimer les contenus qui ne doivent pas être gardés.

    Ainsi, mettons que l'on aient les fichiers d'en-tête
    A.hprenant la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    #ifndef A_H
    #define A_H
    void foo();
    #endif
    B.hprenant la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    #ifndef B_H
    #define B_H
    void bar();
    #endif
    et C.h prenant la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    #ifndef C_H
    #define C_H
    #include <A.h>
    #include <B.h>
    void truc();
    #endif
    ainsi que les fichier d'implémentation (*.cpp)
    A.cpp prenant la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    #include <A.h>
    void foo(){
        /* ce que la fonction foo doit faire */
    }
    B.cpp prenant la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    #include <B.h>
    void bar(){
        /* ce que la fonction foo doit faire */
    }
    C.cpp prenant la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    #include <c.h>
    void truc(){
        /* ce que la fonction truc doit faire */
    }
    et main.cpp prenant la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    #include <A.h>
    #include <B.h>
    #include <C.h>
    int main(){
        foo(); // appelle la fonction foo
        bar(); // appelle la fonction bar
        truc();// appelle la fonction truc
    }
    le fichier C.cpp ressemblerait, une fois que le préprocesseur a fini de travailler, à quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    /* voici  le contenu de C.h  */
    #ifndef C_H
    #define C_H
        /*    qui inclut A.h (dont voici le contenu) */
        #ifndef A_H
        #define A_H
        void foo();
        #endif  //ifndef A_H
        /*    et qui inclut B.h (dont voici le contenu)*/
        #ifndef B_H
        #define B_H
        void bar();
        #endif //ifndef B_H
    void truc();
    #endif //ifndef C_H
    void truc(){
        /* ce que la fonction truc doit faire */
    }
    Quant au fichier main.cpp, il va ressembler à quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    /* j'ai inclut le fichier A.h en premier, en voici le contenu */
    #ifndef A_H
    #define A_H
    void foo();
    #endif
    /* puis j'ai inclut le fichier B.h, en voici le contenu */
    #ifndef B_H
    #define B_H
    void bar();
    #endif
    /* enfin, j'ai inclut le fichier C.h
    #ifndef C_H
    #define C_H
         /* qui inclut A.h, mais dont le contenu est maintenant retiré vu que A_H est déjà défini
         #ifndef A_H    // retiré par le préprocesseur
         #define A_H  // retiré par le préprocesseur
         void foo(); // retiré par le préprocesseur
        #endif // retiré par le préprocesseur
        */
     
         /* qui inclut B.h, mais dont le contenu est maintenant retiré vu que B_H est déjà défini
         #ifndef B_H    // retiré par le préprocesseur
         #define B_H  // retiré par le préprocesseur
         void foo(); // retiré par le préprocesseur
        #endif // retiré par le préprocesseur
        */
        /* par contre le préprocesseur aura laissé ce qui n'apparait que dans C.h */
    void truc();
    #endif  // ifndef C_H
     
    int main(){
        foo(); // appelle la fonction foo
        bar(); // appelle la fonction bar
        truc();// appelle la fonction truc
    }
    2- le compilateur
    Le compilateur est l'outil qui va "traduire" notre code en "code binaire exécutable".

    En réalité, il va traduire le résultat du travail du préprocesseur. Seulement, et c'est sans doute la chose la plus importante à savoir dans le cas présent, il va travailler "fichier par fichier" et oublier de manière systématique tout ce qu'il a pu faire dans le cadre de la compilation des fichiers précédents.

    Ainsi, si ton EDI décide de compiler les fichiers A.cpp, B.cpp, C.cpp et main.cpp dans cet ordre précis, le compilateur ne se souvient absolument pas de ce qu'il a pu faire lors de la compilation de A.cpp lorsqu'il compile B.cpp, ni de ce qu'il a fait lors de la compilation de A.cpp ou de B.cpp lorsqu'il compile C.cpp et, bien sur, il ne se souvient de ce ce qu'il a fait pour aucun des trois premiers fichiers lorsqu'il compile main.cpp.

    S'il accepte que l'on fasse appel à foo(), à bar() ou à truc() dans la fonction main, c'est uniquement parce qu'il sait que ces fonctions existent grâce aux fichier d'en-tête que l'on a inclut (dont le préprocesseur a copié le contenu) et qu'il peut donc en vérifier le prototype pour s'assurer que l'on fournit des informations d'un type correct comme paramètre éventuel et que l'on récupère le retour de la fonction (s'il y en a) dans une donnée du type adéquat.

    3- l'éditeur de liens
    Une fois que tous les fichiers d'implémentation ont été compilés, il faut les regrouper ensemble pour créer notre fichier exécutable, et, le cas échéant, ajouter le code correspondant aux fonctions fournies par les bibliothèques externes (comme la bibliothèque standard) auxquelles notre code fait appel.

    Et ca, c'est le rôle de ce que l'on appelle "l'éditeur de liens".
    Pour faire simple, il va prendre l'ensemble de tous les fichiers objets générés par le compilateur, les assembler "les uns après les autres" dans un fichier unique, et "s'amuser" à trouver l'adresse de début de chaque fonction dont le code binaire est fournit (que ce soit par nos fichiers objets ou par une bibliothèque externe), puis il va transformer chaque appel à une fonction particulière en un appel "pointant" vers l'adresse à laquelle se trouve la fonction dans l'exécutable final.

    Seulement, pour que l'éditeur de liens puisse faire correctement son travail, il y a deux conditions à remplir:
    1. Il doit disposer du code binaire exécutable de toutes les fonctions auxquelles le code fait appel: s'il ne sait pas faire correspondre un appel avec l'adresse de la fonction équivalente, il va forcément s'arrêter sur une erreur
    2. le code binaire exécutable de chaque fonction ne doit apparaître qu'une seule et unique fois (*), autrement, il est dans l'incapacité de déterminer s'il doit faire appel à l'adresse la première ou à celle de la deuxième version de la fonciton

    Ces deux conditions sont généralement exposées sous les termes de "One definition Rule" (règle "une seule définition").

    En outre, il n'y a finalement absolument aucune différence entre une fonction et une donnée globale, vu que l'exécutable va y accéder exactement de la même manière (en allant à l'adresse mémoire qui correspond à la fonction ou à la donnée). Si bien que les mêmes causes auront les mêmes effets: si l'éditeur de liens trouve la définition de deux données globales (ou plus) portant le même nom (parce que la donnée a été définie dans A.cpp / A.obj et dans main.cpp / main.o à cause des inclusions des fichiers d'en-tête), il ne va pas aimer du tout, et n'aura d'autre choix que de s'arrêter sur une erreur.

    (*) Après, il est vrai qu'il est possible de forcer l'éditeur de liens à accepter de se retrouver avec plusieurs définitions de la même fonction, en la déclarant inline. Seulement, a ce moment là, c'est (normalement) carrément tout le code exécutable de la fonction qui remplace la "procédure d'appel" de celle-ci

    Au final:
    Dans le fichier fonctions.h, tu définis deux variables globales (lastClick et select).

    A cause du jeu des inclusions de fichiers, ces variables vont donc être définies deux fois: une fois dans fonction.obj (le fichier objet généré à partir de fonctions.cpp) et une fois dans main.obj (le fichier objet généré à partir de main.cpp).

    Et, du coup, l'éditeur de liens n'a pas d'autre choix que de se plaindre d'une définition multiple.

    La solution pour éviter cette définition multiple est de clairement indiquer au compilateur que les noms lastClick et select qui arrivent dans le code suite à l'inclusion du fichier d'en-tête fonctions.h ne servent réellement qu'à lui indiquer qu'il exite -- effectivement -- "quelque part dans le code" (sans doute dans le code du fichier fonction.cpp) des variables globales nommées respectivement lastClick et select (qui sont elles correctement définies) en les déclarant extern dans le fichier d'en-tête.

    Cela nous donnerait quelque chose ressemblant à
    fonction.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    #ifndef FONCTIONS_H
    #define FONCTIONS_H 
    /* ... */
     
    extern bool lastClick; // il existe une variable nommée lastKlick "quelque par dans le code"
    bool select; // ainsi qu'une variable nommée "select". 
    /* ... */
    #endif
    fonctions.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    #include <fonction.h>
    bool lastClick = false; //la définition "réelle" de lastClick
    bool select = true;// et celle de select
    /* ... */
    Mon grain de sel
    Le but de cette (très longue) intervention était avant tout de te permettre de comprendre comment les différents outils fonctionnent, afin de pouvoir cerner plus facilement l'erreur par toi-même.

    La solution que j'ai donnée n'est donc pas représentative de ce que je considère comme étant une "bonne pratique", ni même seulement une "pratique acceptable": c'est juste une pratique "possible" à utiliser de manière très ponctuelle pour contourner un problème très particulier mais qui, en l'espèce, ne devrait être choisie qu'en tout dernier recours, lorsque les autres possibilités s'avèrent réellement impossibles à mettre en oeuvre

    Je en effet exactement du même avis que @kaitlyn: les variables globales sont une source faramineuse de problème et il vaut mieux s'en passer à chaque fois que possible.

    Or, dans le cas présent, comme tes variables lastClick et select ne sont de toutes manière utilisées que dans la fonction gestionBoutons, tu pourrais très bien faire en sorte d'en limiter la portée à la seule fonction gestionBoutons, tout en gardant leur valeur entre les différents appels à la fonction en les déclarant statiques, sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    void gestionBoutons(const sf::Window &target, Bouton &bout1, Bouton &bout2, Recepteur &rec1)
    {
        static bool lastClick = false;
        static bool select = false;
        /* le reste de la fonction */
    }
    Par contre, cette fonction présente,à mon sens, un problème majeur: elle te limite forcément à deux boutons, ni plus, ni moins, ce qui sera très bien pour un message avec un bouton "ok" et un bouton "annuler", mais qui ne t'autorisera pas à ajouter un bouton "réessayer", par exemple, sans devoir réécrire un code qui, en gros, serait très largement identique

    De plus, cela pose la question du nombre de bouton que l'on décide de prévoir:
    • est-ce que un bouton seul est possible
    • on part d'office avec deux boutons
    • qu'en est-il pour trois, quatre, cinq, quinze, ... 150 boutons [
    • A combien va-t-on s'arrêter
    • oui, et si ... on veut en mettre "un de plus"
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

Discussions similaires

  1. Réponses: 19
    Dernier message: 13/09/2016, 23h34
  2. Directives du préprocesseur
    Par edgarjacobs dans le forum Débuter
    Réponses: 9
    Dernier message: 12/05/2012, 12h26
  3. Réponses: 13
    Dernier message: 02/08/2009, 19h06
  4. Directive préprocesseur #define
    Par Altrensa dans le forum C
    Réponses: 12
    Dernier message: 29/11/2007, 17h53
  5. Réponses: 6
    Dernier message: 19/02/2007, 11h13

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo