Partage
  • Partager sur Facebook
  • Partager sur Twitter

Erreur de débutant - Compilation G++ (macOS)

Sujet résolu
    21 mai 2019 à 16:26:18

    Bonjour, je débute en c++, et là j'essaye de me faire une todo-list en CLI. Je ne vous cache pas que comme je suis débutant, je me passerais bien des objets, mais lorsque j'utilise juste plus de 1 fonction, mon IDE, attend le constructeur des autres fonctions. Bref du coup j'ai fait ce code:

    main.cpp:

    #include <stdlib.h> 
    #include <iostream>
    #include <string>
    #include <fstream>
    #include "../include/todo.hpp"
    
    int main(int argc, const char *argv[]) {
        //Set global filepath var.
        std::string const fileName("./todo.txt");
    
        todo item("");
    
    
        if(argc < 2) {
            std::cout << "Usage: todo <parameter>!" << std::endl;
            return -1;
        }
        if(std::string(argv[1]) == "todo" && std::string(argv[2]) == "-a") {
            item.add(fileName, argv[3]);
        }
        if(std::string(argv[1]) == "todo" && std::string(argv[2]) == "-d") {
            item.del(fileName, atoi(argv[3]));
        }
    
        return 0;
    }

    todo.cpp:

    #include <stdlib.h> 
    #include <iostream>
    #include <string>
    #include <fstream>
    
    #include "../include/todo.hpp"
    
    void todo::add(std::string file, std::string arg) {
        
        std::ofstream monFlux(std::string(file), std::ios::app);
    
        if(monFlux) {
            monFlux << arg << std::endl;
        }
        else {
            std::cout << "ERREUR: Impossible d'ouvrir le fichier." << std::endl;
        }
    }
    
    void todo::del(std::string file, int line_erase) {
        std::string Buffer = ""; //Variable contenant le texte à réécrire dans le fichier
        std::ifstream ReadFile(file);
        if (ReadFile) { //Si le fichier est trouvé
            std::string line;
            int Line = 0;
            while (std::getline(ReadFile, line)) { //on parcours le fichier et on initialise line à la ligne actuelle
                Line++;
                if(Line != line_erase) //Si la ligne atteinte est différente de la ligne à supprimer...
                    Buffer += line + "\n"; //On ajoute le contenu de la ligne dans le contenu à réécrire
            }
        }
     
        std::ofstream WriteFile(file); //On ouvre ce même fichier en écriture
        WriteFile << Buffer; //On écris le texte dedans
    }

    todo.hpp:

    #ifndef todo_hpp
    #define todo_hpp
    
    #include <stdlib.h> 
    #include <iostream>
    #include <string>
    #include <fstream>
    
    //functions prototypes
     class todo {
    
     public:
        todo();
        todo(std::string empty);
        void add(std::string file, std::string arg);
        void del(std::string file, int line_erase);
    
    private:
        std::string file, arg;
        int line_erase;
     }; // todo
     #endif /*todo_hpp*/




    Et lorsque je compile : "g++ -c main.cpp -o main.o -I." | "g++ -c todo.cpp -o todo.o -I." | "g++ main.o todo.o -o myexe", voici ce que j’obtiens :

    "Undefined symbols for architecture x86_64:

    "todo::todo(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)", referenced from:
    _main in main.o
    ld: symbol(s) not found for architecture x86_64
    clang: error: linker command failed with exit code 1 (use -v to see invocation)"


    Comment puis-je faire ? (Merci d'avance pour votre aide)

    -
    Edité par romainledeveloppeur 21 mai 2019 à 16:28:14

    • Partager sur Facebook
    • Partager sur Twitter
    R. Le développeur
      21 mai 2019 à 16:52:53

      Où sont définis les constructeurs de todo ?

      Sur le code:

      • Il n'y a aucune raison d'utiliser stdlib.h.
      • pour quoi reconstruire un std::string dans std::ofstream monFlux(std::string(file), std::ios::app); ? file en est déjà un.
      • Dans cette expression std::string(argv[1]) == "todo" (et celle similaire), std::string_view serait préférable ou même argv[1] == "todo"sv
      • c'est étrange que ton todo prenne le nom du fichier en argument de chaque fonction, pourquoi ne pas en avoir fait un membre de la classe ? Ce serait probablement plus simple de lire le fichier une fois dans un std::vector, faire les manips dessus puis le sauvegarder quand c'est nécessaire.
      • contrairement à ce que laisse penser la signature du main, la valeur de retour doit être dans l'intervalle 0 à 256. Donc moins va être transformer et le code d'erreur ne correspondra pas. En plus, si le code est divisible par 256, il sera transformé en 0 -> pas d'erreur.

      -
      Edité par jo_link_noir 21 mai 2019 à 17:02:40

      • Partager sur Facebook
      • Partager sur Twitter
        21 mai 2019 à 17:42:35

        Merci beaucoup pour votre aide ;)

        J'ai modifié le code selon vos conseils, mais malheureusement l'erreur à la compilation persiste, d’où peut-elle venir ?

        Pourriez-vous m’éclairer sur ça provenance exacte, si vous les savez bien-sûr ;) ?

        main.cpp:

        #include <iostream>
        #include <string>
        #include <fstream>
        #include "../include/todo.hpp"
        
        int main(int argc, const char *argv[]) {
            //Set global filepath var.
            std::string const fileName("./todo.txt");
        
            todo item("");
        
        
            if(argc < 2) {
                std::cout << "Usage: todo <parameter>!" << std::endl;
                return 2;
            }
            if(std::string_view(argv[1]) == "todo" && std::string_view(argv[2]) == "-a") {
                item.add(fileName, argv[3]);
            }
            if(std::string_view(argv[1]) == "todo" && std::string_view(argv[2]) == "-d") {
                item.del(fileName, atoi(argv[3]));
            }
        
            return 0;
        }

        todo.cpp:

        #include <iostream>
        #include <string>
        #include <fstream>
        
        #include "../include/todo.hpp"
        
        void todo::add(std::string file, std::string arg) {
            
            std::ofstream monFlux(file, std::ios::app);
        
            if(monFlux) {
                monFlux << arg << std::endl;
            }
            else {
                std::cout << "ERREUR: Impossible d'ouvrir le fichier." << std::endl;
            }
        }
        
        void todo::del(std::string file, int line_erase) {
            std::string Buffer = ""; //Variable contenant le texte à réécrire dans le fichier
            std::ifstream ReadFile(file);
            if (ReadFile) { //Si le fichier est trouvé
                std::string line;
                int Line = 0;
                while (std::getline(ReadFile, line)) { //on parcours le fichier et on initialise line à la ligne actuelle
                    Line++;
                    if(Line != line_erase) //Si la ligne atteinte est différente de la ligne à supprimer...
                        Buffer += line + "\n"; //On ajoute le contenu de la ligne dans le contenu à réécrire
                }
            }
         
            std::ofstream WriteFile(file); //On ouvre ce même fichier en écriture
            WriteFile << Buffer; //On écris le texte dedans
        }

        todo.hpp:

        #ifndef todo_hpp
        #define todo_hpp
        
        #include <stdlib.h> 
        #include <iostream>
        #include <string>
        #include <fstream>
        
        //functions prototypes
         class todo {
        
         public:
            todo();
            todo(std::string empty);
            void add(std::string file, std::string arg);
            void del(std::string file, int line_erase);
        
        private:
            std::string file, arg;
            int line_erase;
         }; // todo
         #endif /*todo_hpp*/

        Erreur:

        "Undefined symbols for architecture x86_64:

          "todo::todo(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)", referenced from:

              _main in main.o

        ld: symbol(s) not found for architecture x86_64

        clang: error: linker command failed with exit code 1 (use -v to see invocation)"

        Encore une fois, merci pour votre aide :)




        • Partager sur Facebook
        • Partager sur Twitter
        R. Le développeur
          21 mai 2019 à 21:19:00

          Je le répète encore une fois: où sont définis les constructeurs de todo ?

          • Partager sur Facebook
          • Partager sur Twitter
            22 mai 2019 à 0:15:55

            Mince, je savais pas que je devais en créer, c'est vraiment parce que je suis en plein apprentissage du C++ :(.

            Je vais tenter d'en faire, du coup.

            • Partager sur Facebook
            • Partager sur Twitter
            R. Le développeur
              22 mai 2019 à 8:48:26

              Salut,

              romainledeveloppeur a écrit:

              Mince, je savais pas que je devais en créer,

              Et pourtant, cela paraît logique: même si on leur donne un nom particulier (constructeur), même si, de manière tout à fait exceptionnelle (parce que l'on parle d'un constructeur, justement), le compilateur accepte que l'on ne fournisse aucun type de retour, le constructeur reste ... une fonction; même s'il s'agit d'une fonction "toute particulière".

              Or, tu indique explicitement au compilateur qu'il existe une fonction nommée todo qui ne prend aucun paramètre et une autre (du même nom) qui prend une std::string en paramètre.

              Le compilateur n'aura donc pas d'autre choix que de te croire sur parole, et il profitera de ce que tu lui a dit pour s'assurer... que tu fais correctement appel aux fonctions indiquées en leur fournissant des données cohérentes par rapport aux paramètre que tu as dit qu'elles s'attendaient à recevoir ;)

              Après la génération des fichiers objets, l'éditeur de liens va prendre le relais: il va regrouper le code binaire exécutable de tous les fichiers objets qu'on lui indique et remplacer tous les appels aux différentes fonctions par un appel à l'adresse mémoire du début de la fonction appelée dans l'exécutable.

              S'il ne trouve pas l'adresse mémoire à laquelle commence une fonction quelconque, il n'aura pas d'autre choix: il devra

              1. t'engueuler, parce que ce n'est décidément pas normal
              2. abandonner ce qu'il fait, vu que, autrement, le résultat ne sera pas cohérent
              • Partager sur Facebook
              • Partager sur Twitter
              Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                22 mai 2019 à 18:13:15

                Merci beaucoup pour votre réponse ;)

                Mais non, en fait l'objet n'était pas adapté à mon projet, en tout cas pour le moment.

                J'ai donc fait ceci à la place, en utilisant simplement des fonctions void.

                #include <iostream>
                #include <string>
                #include <fstream>
                #include <stdio.h>
                
                void add(std::string file, std::string prm) {
                    std::ofstream monFlux(file, std::ios::out);
                
                    if(monFlux) {
                        monFlux << prm << std::endl;
                    }
                    else {
                        std::cout << "ERREUR: Impossible d'ouvrir le fichier." << std::endl;
                    }
                }
                void del(std::string file, int ln) {
                    std::string Buffer = ""; //Variable contenant le texte à réécrire dans le fichier
                    std::ifstream ReadFile(file);
                    if (ReadFile) { //Si le fichier est trouvé
                        std::string line;
                        int Line = 0;
                        while(std::getline(ReadFile, line)) { //on parcours le fichier et on initialise line à la ligne actuelle
                            Line++;
                            if(Line != ln) //Si la ligne atteinte est différente de la ligne à supprimer...
                                Buffer += line + "\n"; //On ajoute le contenu de la ligne dans le contenu à réécrire
                        }
                    } else {
                        std::cout << "ERREUR: Impossible d'ouvrir le fichier." << std::endl;
                    }
                 
                    std::ofstream WriteFile(file); //On ouvre ce même fichier en écriture
                    WriteFile << Buffer; //On écris le texte dedans
                }
                void ls(std::string file) {
                    std::ifstream ReadFile(file);
                    if(ReadFile) {
                        std::string line;
                        while(std::getline(ReadFile, line)) {
                                std::cout << line << std::endl;  // on l'affiche
                        }
                    } else {
                        std::cout << "ERREUR: Impossible d'ouvrir le fichier." << std::endl;
                    }
                }
                
                int main(int argc, const char *argv[]) {
                    //Set global filepath var.
                    std::string const fileName("./todo.txt");
                
                    if(argc < 2) {
                        std::cout << "Usage: todo <parameter>!" << std::endl;
                        return -1;
                    }
                    if(std::string(argv[1]) == "todo" && std::string(argv[2]) == "-a") {
                        add(fileName, argv[3]);
                    }
                    if(std::string(argv[1]) == "todo" && std::string(argv[2]) == "-d") {
                        del(fileName, atoi(argv[3]));
                    }
                    if(std::string(argv[1]) == "todo" && std::string(argv[2]) == "-ls") {
                        ls(fileName);
                    }
                
                    return 0;
                }

                Ca fonctionne, seulement le gros casse-tête dans tout sa, est que lorsque j'ajoute une phrase complète via mon programme, il ne m'ajoute au fichier todo.txt que l'argument qui suit argv[2], donc à savoir argv[3], mais du coup tous le reste de ma phrase est ignoré, et n'est donc pas inscrit dans le fichier todo.txt . Je cherche donc une solution pour qu'il ne considère pas le reste de ma phrase comme plusieurs arguments, mais seulement 1 seul. J'ai pensé que mettre entre guillemets, la phrase serait peut-être plus simple à considérer comme un seul arguments, mais pour l'instant je n'ai pas de solution...

                • Partager sur Facebook
                • Partager sur Twitter
                R. Le développeur

                Erreur de débutant - Compilation G++ (macOS)

                × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                • Editeur
                • Markdown