Partage
  • Partager sur Facebook
  • Partager sur Twitter

a

a

Sujet résolu
2 avril 2023 à 22:41:01

a

-
Edité par 238 7 juillet 2024 à 11:33:03

  • Partager sur Facebook
  • Partager sur Twitter
2 avril 2023 à 23:28:19

On ne met jamais les implémentations dans les headers, sauf si elles sont statiques ou inline ou des templates. Tu ne montres pas ton projet complet, mais en regardant les erreurs on voit bien que tu as d'autres fichiers .cpp qui se sont retrouvés avec ton fichier Utils.hpp, ce qui conduit à une violation ODR: https://en.wikipedia.org/wiki/One_Definition_Rule

  • Partager sur Facebook
  • Partager sur Twitter
2 avril 2023 à 23:45:03

au passage, gnu-make a déjà une variable CXXFLAGS pour les flags de compilation C++. Et CPPFLAGS pour le préprocesseur (les -I): il est donc inutile d'en inventer une autre.

Si on regarde ce qui est prédéfini pour CXX :

LINK.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)
CXX = g++
COMPILE.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c

avec les recettes

%: %.cc
	$(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@

%.o: %.cc
	$(COMPILE.cc) $(OUTPUT_OPTION) $<


EDIT il y  a aussi un bug malheureusement courant : les dépendances envers les fichiers d'entete ne sont pas indiquées. 

Si apres une compilation, on modifie Game.hpp, ca ne forcera pas une re-compilation. A moins de tout recompiler systématiquement, mais dans ce cas la commande make est plus un problème qu'une solution (*) : il est alors plus simple de faire un script   genre   g++ *.cpp  qui recompile tout d'un bloc

(*) on s'en sert généralement pour aider à gérer  les compilations séparées, en réduisant les re-compilations au strict nécessaire. Qui n'est pas fait ici.

-
Edité par michelbillaud 3 avril 2023 à 8:01:52

  • Partager sur Facebook
  • Partager sur Twitter
3 avril 2023 à 20:10:41

a

-
Edité par 238 7 juillet 2024 à 11:33:09

  • Partager sur Facebook
  • Partager sur Twitter
3 avril 2023 à 22:50:52

L'erreur est assez explicite, Utils::hconsole est défini plusieurs fois indirectement (car défini dans un header, ce qui est un "red flag" de base). Tu ne peux pas définir une variable non-statique dans plusieurs unités de compilations (je précise: variables en dehors d'une fonction ou d'une classe)

Tu peux en faire une variable globale (mot clé extern), tu la déclares dans un header, et tu la définis dans une seule unité de compilation. Mais c'est aussi une mauvaise idée en générale, même si autorisé, souvent le signe d'un design peu inspiré.

Et inclure windows.h dans un header, je dirais pas que c'est un red flag, mais pas loin pour moi tellement ce header est une calamité.

-
Edité par SpaceIn 3 avril 2023 à 22:53:01

  • Partager sur Facebook
  • Partager sur Twitter
3 avril 2023 à 23:19:47

Autrement dit : il devrait y avoir Utils.hpp et Utils.cpp

Parce que la variable Utils::hConsole doit etre déclarée dans un fichier d'entete qui est inclus plusieurs fois, mais il ne faut la DEFINIR qu'une seule fois.

-
Edité par michelbillaud 3 avril 2023 à 23:22:58

  • Partager sur Facebook
  • Partager sur Twitter
7 avril 2023 à 14:41:52

a

-
Edité par 238 7 juillet 2024 à 11:33:13

  • Partager sur Facebook
  • Partager sur Twitter
7 juillet 2024 à 14:58:34

Bonjour, on n'efface pas ses message après avoir reçu de l'aide, je recopie ci-après les messages d'origine pour archive et ferme ce sujet.

238 a écrit:

Bonjour à vous,

J'ai un problème avec mon code lorsque j'essaye d'ajouter en #include mon fichier "Utils.hpp" à mon fichier "ChessGraphic.hpp" ou à "Game.hpp".

Par contre aucun probléme lors de l'ajout de cet include à mon "main.cpp"

Voici l'erreur :

PS D:\Programming\Chess> make
 
g++ src/Sourcefiles/main.o src/Sourcefiles/ChessGraphic.o src/Sourcefiles/Game.o -o main -LC:\SFML-2.5.1\lib -lsfml-graphics -lsfml-window -lsfml-system -lsfml-audio
src/Sourcefiles/ChessGraphic.o: In function `std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_data()
const':
D:/Programming/Chess/src/Headerfiles/Utils.hpp:9: multiple definition of `Utils::PrintInfo(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
src/Sourcefiles/main.o:D:/Programming/Chess/src/Headerfiles/Utils.hpp:9: first defined here
src/Sourcefiles/ChessGraphic.o: In function `Utils::PrintError(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)':
D:/Programming/Chess/src/Headerfiles/Utils.hpp:16: multiple definition of `Utils::PrintError(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
src/Sourcefiles/main.o:D:/Programming/Chess/src/Headerfiles/Utils.hpp:16: first defined here
src/Sourcefiles/Game.o: In function `std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_data() const':
D:/Programming/Chess/src/Headerfiles/Utils.hpp:9: multiple definition of `Utils::PrintInfo(std::__cxx11::basic_string<char, std::char_trsrc/Sourcefiles/main.o:D:/Programming/Chess/src/Headerfiles/Utils.hpp:9: first defined here
src/Sourcefiles/Game.o: In function `Utils::PrintError(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)':
D:/Programming/Chess/src/Headerfiles/Utils.hpp:16: multiple definition of `Utils::PrintError(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
src/Sourcefiles/main.o:D:/Programming/Chess/src/Headerfiles/Utils.hpp:16: first defined here
collect2.exe: error: ld returned 1 exit status
make: *** [Makefile:22: link] Error 1

Et voici mes fichiers :

main.cpp

#include <iostream>
#include <windows.h>
#include "../Headerfiles/Game.hpp"
#include "../Headerfiles/Utils.hpp" /* Fonctionne parfaitement ici */
 
int main()
{
    // ...
    return 0;
}

Utils.hpp

#ifndef UTILS_HPP
#define UTILS_HPP
 
#include <iostream>
#include <windows.h>
#include <string>
 
namespace Utils{
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
 
    void PrintInfo(const std::string &text){
        SetConsoleTextAttribute(hConsole, 2);
        std::cout << text;
        SetConsoleTextAttribute(hConsole, 7);
    }
 
    void PrintError(const std::string &text){
        SetConsoleTextAttribute(hConsole, 12);
        std::cerr << text;
        SetConsoleTextAttribute(hConsole, 7);
    }
      
}
 
#endif

Game.hpp

#ifndef GAME_HPP
#define GAME_HPP
 
#include <iostream>
#include "ChessGraphic.hpp"
#include "Utils.hpp" /* Provoque l'erreur lors de l'ajout */
 
class Game {
public:
    Game();
private:
    ChessGraphic _chessGraphic;
};
 
#endif /* GAME_HPP */
#ifndef CHESSGRAPHIC_HPP
#define CHESSGRAPHIC_HPP
 
#include <SFML/Graphics.hpp>
#include "../Headerfiles/Utils.hpp" /* Provoque une erreur ici aussi */
 
class ChessGraphic{
public:
    ChessGraphic();
    void update();
private:
    sf::RenderWindow window;
};
 
#endif

et le makefile 

CXX = g++
CXXCFLAGS = -std=c++17 -Wall -Wextra -Werror -pedantic -g
SRC_DIR = src/Sourcefiles
INC_DIR = C:\SFML-2.5.1\include
LIB_DIR = C:\SFML-2.5.1\lib
LIBS = -lsfml-graphics -lsfml-window -lsfml-system -lsfml-audio
 
# Obtention de la liste de tous les fichiers .cpp dans le répertoire SRC_DIR
SRCS := $(wildcard $(SRC_DIR)/*.cpp)
 
# Creation de la liste des fichiers objets correspondants
OBJS := $(patsubst $(SRC_DIR)/%.cpp, $(SRC_DIR)/%.o, $(SRCS))
 
all: compile link clean execute
 
compile: $(OBJS)
 
$(SRC_DIR)/%.o: $(SRC_DIR)/%.cpp
    $(CXX) -c $< -o $@ $(CXXCFLAGS) -I$(INC_DIR)
 
link: $(OBJS)
    $(CXX) $^ -o main -L$(LIB_DIR) $(LIBS)
 
clean:
    cd src/Sourcefiles && del *.o
 
execute:
    ./main.exe

Je suppose que l'erreur vient de mon HANDLE avec l'ajout de windows.h, mais je n'arrive pas à comprendre l'erreur.

Merci par avance pour votre aide. 




-
Edité par 238 2 avril 2023 à 22:44:36

---------------------------------------------------

Merci pour vos réponses.

J'avoue ne pas encore comprendre précisément ce qui se trame. J'ai essayé avec les deux fonctions en inline mais tjrs une erreur :

PS D:\Programming\Chess> make
cd src/Sourcefiles && del *.o
Could Not Find D:\Programming\Chess\src\Sourcefiles\*.o
g++ -c src/Sourcefiles/main.cpp -o src/Sourcefiles/main.o -std=c++17 -Wall -Wextra -Werror -pedantic -g -IC:\SFML-2.5.1\include
g++ -c src/Sourcefiles/ChessGraphic.cpp -o src/Sourcefiles/ChessGraphic.o -std=c++17 -Wall -Wextra -Werror -pedantic -g -IC:\SFML-2.5.1\include
g++ -c src/Sourcefiles/Game.cpp -o src/Sourcefiles/Game.o -std=c++17 -Wall -Wextra -Werror -pedantic -g -IC:\SFML-2.5.1\include
g++ src/Sourcefiles/main.o src/Sourcefiles/ChessGraphic.o src/Sourcefiles/Game.o -o main -LC:\SFML-2.5.1\lib -lsfml-graphics -lsfml-window -lsfml-system -lsfml-audio
src/Sourcefiles/ChessGraphic.o: In function `std::__cxx11::basic_string<unsigned int, std::char_traits<unsigned int>, std::allocator<unsigned int> >::_M_is_local() const': 
C:/MinGW/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/bits/basic_string.h:210: multiple definition of `Utils::hConsole'
src/Sourcefiles/main.o:main.cpp:(.bss+0x0): first defined here
src/Sourcefiles/Game.o: In function `Utils::PrintInfo(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)':
D:\Programming\Chess/src/Sourcefiles/../Headerfiles/Utils.hpp:13: multiple definition of `Utils::hConsole'
src/Sourcefiles/main.o:main.cpp:(.bss+0x0): first defined here
collect2.exe: error: ld returned 1 exit status
make: *** [Makefile:22: link] Error 1

Voici le code entièrement :

// main.cpp
#include <iostream>
#include "../Headerfiles/Game.hpp"
 
int main()
{
    bool isPlaying { true };
    Game game;
 
    while(isPlaying){
        game.play();
    }
 
    return 0;
}
// Game.cpp
#include "../Headerfiles/Game.hpp"
 
Game::Game(){
    Utils::PrintInfo("[OK] Message d'information");
}
 
void Game::play(){
    _chessGraphic.update();
}
// Game.hpp
 
#ifndef GAME_HPP
#define GAME_HPP
 
#include "ChessGraphic.hpp"
#include "Utils.hpp"
 
class Game {
public:
    Game();
    void play();
private:
    ChessGraphic _chessGraphic;
};
 
#endif /* GAME_HPP */
// Graphics.hpp
 
#ifndef CHESSGRAPHIC_HPP
#define CHESSGRAPHIC_HPP
 
#include <SFML/Graphics.hpp>
#include "Utils.hpp"
 
class ChessGraphic{
public:
    ChessGraphic();
    void update();
    sf::RenderWindow& getWindow();
private:
    sf::RenderWindow window;
};
 
#endif
// Graphics.cpp
 
#include "../Headerfiles/ChessGraphic.hpp"
 
ChessGraphic::ChessGraphic() : window(sf::VideoMode(200, 200), "Chess"){
 
}
 
void ChessGraphic::update(){
    sf::CircleShape shape(100.f);
    shape.setFillColor(sf::Color::Red);
 
    sf::Event event;
    while (window.pollEvent(event)){
        if (event.type == sf::Event::Closed)
            window.close();
    }
 
    window.clear();
    window.draw(shape);
    window.display();
}
// Utils.h
 
#ifndef UTILS_HPP
#define UTILS_HPP
 
#include <iostream>
#include <windows.h>
#include <string>
 
namespace Utils{
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
 
    inline void PrintInfo(const std::string &text){
        SetConsoleTextAttribute(hConsole, 2);
        std::cout << text;
        SetConsoleTextAttribute(hConsole, 7);
    }
 
    inline void PrintError(const std::string &text){
        SetConsoleTextAttribute(hConsole, 12);
        std::cerr << text;
        SetConsoleTextAttribute(hConsole, 7);
    }
      
}
 
#endif

Je ne vois pas ou la règle du ODR est violée. 

Je pense avoir un problème concernant l'utilisation des headers. On implémente le .hpp dans le .cpp, et on ajoute les dépendances dans le .h, mais apparemment non.

@michelbillaud merci pour l'info, en effet, je n'étais pas sûr à 100% que mon code était correct. J'ai appris un peu sur le tas cette semaine makefile.

Merci encore à vous deux.

-----------------------------------------------

C'est noté, j'ai finalement compris. J'ai tout simplement oublié que j'étais dans le scope global, et j'ai du relire plusieurs fois et doucement la ligne "(je précise: variables en dehors d'une fonction ou d'une classe)"


Merci à vous deux pour le coup de main.

  • Partager sur Facebook
  • Partager sur Twitter