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

C++ Discussion :

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


Sujet :

C++

  1. #1
    Membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2016
    Messages
    173
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Finistère (Bretagne)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2016
    Messages : 173
    Points : 50
    Points
    50
    Par défaut Erreur de débutant - Compilation G++ (macOS)
    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:
    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
    #include <stdlib.h> 
    #include <iostream>
    #include <string>
    #include <fstream>
    #include "../include/main_functions.hpp"
    #include "./todo.cpp"
     
    using namespace std;
     
    int main(int argc, const char *argv[]) {
        //Set global filepath var.
        string const fileName("./todo.txt");
     
        todo item("");
     
     
        if(argc < 2) {
            cout << "Usage: todo <parameter>!" << 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:
    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 <stdlib.h> 
    #include <iostream>
    #include <string>
    #include <fstream>
     
    #include "../include/main_functions.hpp"
     
    using namespace std;
     
    void todo::add(string file, string arg) {
     
        ofstream monFlux(file.c_str(), ios::app);
     
        if(monFlux) {
            monFlux << arg << endl;
        }
        else {
            cout << "ERREUR: Impossible d'ouvrir le fichier." << endl;
        }
    }
     
    void todo::del(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
            }
        }
        ReadFile.close(); //On ferme le fichier en lecture
     
        std::ofstream WriteFile(file); //On ouvre ce même fichier en écriture
        WriteFile << Buffer; //On écris le texte dedans
        WriteFile.close(); //et on ferme le fichier
    }
    main_functions.hpp:
    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 main_functions_hpp
    #define main_functions_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 /*main_functions_hpp*/
    Et lorsque je compile : g++ main.cpp -o todo, voici ce que j’obtiens :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    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-d3f965.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)

  2. #2
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Salut,

    Quand tu veux compiler plusieurs fichiers séparément, de manière à générer les fichiers objets, mais sans provoquer l'édition de liens, tu dois ajouter le paramètre -c à ta ligne de commandes:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    g++ -c main.cpp -o main.o -I. 
    g++ -c todo.cpp -o todo.o -I.
    g++ main.o todo.o -o myexe
    NOTA:
    1- on n'inclut JAMAIS un fichier d'implémentation (*.cpp), NULLE PART (et surtout pas dans un autre fichier d'implémentation). Il n'y a que les fichier d'EN-TETE (*.hpp) qui peuvent être inlus à l'aide de la directive #include.

    2- Idealement, on n'indiquera que le nom du fichier d'en-tête qui devra être inclu (ex : #include <main_functions.hpp> et on indiquera le dossier dans lequel il faut aller les chercher sous forme de paramètres dans la ligne de commande:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    g++ -c main.cpp -o main.o -I../include
    3- La directive using namespace std; ne devrait jamais être utilisée. Les raisons en ont été expliquées en long, en large et en travers sur le forum. Je te laisse faire ta propre recherche pour les comprendre

    4- Idéalement, on va essayer d'avoir une certaine cohérence dans les noms entre les fichiers d'implémentation (*.cpp) et les fichiers d'en-tête (*.hpp). C'est particulièrement vrai lorsque l'on défini une classe : Il est beaucoup plus facile de s'y retrouver lorsque le nom du fichier d'en-tête et du fichier d'implémentation correspond au nom de la classe que l'on crée. Au lieu d'appeler ton fichier d'en-tête main_funcitons.hpp (qui n'est pas un mauvais nom, au demeurant), pourquoi ne l'appellerais tu pas, tout simplement todo.hpp vu qu'il définit... la classe todo

    5- Depuis C++11 (qui date déjà de huit ans maintenant), il y a un constructeur pour std::ifstream (ainsi que pour std::ofstream) qui accepte de recevoir le nom du fichier sous la forme d'une std::string. Il n'y a donc plus besoin de faire appel à la fonction membre c_str de std::string afin de lui transmettre une chaîne de caractères "C style"

    6- Que ce soit pour la classe std::ifstream ou pour la classe std::ofstream, le destructeur s'assure que tout fichier ouvert sera correctement fermé lorsque l'instance de la classe est détruite. Or, si tu crées une instance de l'une ou l'autre de ces classes à l'intérieur d'une fonction, les règles de portée font que cette instance sera détruite (et son destructeur appelé automatiquement) lorsque l'on atteind l'accolade fermante } correspondant à la portée dans laquelle l'instance de la classe a été déclarée.

    Il n'y a donc, a priori, absolument aucune raison d'appeler explicitement la fonction membre close() dans ce cas.


    7- Dés que ton projet nécessite plus d'un fichier pour fournir le résultat final, tu as sans très largement intérêt à "automatiser" la compilation, car les instructions qu'il faudra lancer pour obtenir l'exécutable final vont avoir tendances à se multiplier comme les petits pains et à devenir de plus en plus complexes.

    La solution "classique" est d'utiliser un outil d'automatisation de la compilation appelé make, qui travaille avec un fichier texte décrivant le processus de compilation dans son ensemble appelé Makefile. Mais l'écriture d'un tel fichier devient rapidement longue, fastidieuse et source d'erreurs, surtout lorsque ton projet commence à grandir et à nécessiter des dépendances qui pourraient ne pas être remplie.

    L'idéal est donc de passer par un outil de description et de configuration de ton projet qui pourra, sur base de la description du projet, générer un (ou plusieurs) Makefile. Les outils "historiques" pour une telle descriptions sont appelés autotools sous linux (et regroupent autoconf automake, autoheaders et libtool), mais restent assez rudes à l'emploi (en plus, ils ne sont pas "facilement utilisables" sous windows ).

    Depuis quelques années, nous disposons par contre d'un outil, que l'on peut utiliser aussi bien sous linux, sous mac ou sous windows, appelé CMake qui fait parfaitement le travail -- qui est utilisé entre autres par les IDE clion, et xcode (que l'on trouve sous Mac), du moins, si je ne m'abuse -- mais qui peut également être utilisé en ligne de commandes (si tu tiens absolument à coder "à l'ancienne" ).

    Je te conseillerais vivement de t'intéresser à cet outil, car, même si cela implique qu'il y aura une petite étape d'apprentissage pour être en mesure de l'utiliser, cela te facilitera énormément la vie par la suite.

  3. #3
    Membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2016
    Messages
    173
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Finistère (Bretagne)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2016
    Messages : 173
    Points : 50
    Points
    50
    Par défaut
    Tout d'abord je vous remercie, beaucoup pour votre aide, très précise, et détaillée (ça vous a pris surement pas mal de temps ).

    J'ai modifier le code, mais en vain, l'erreur apparaît toujours, je ne vois pas en quoi j'édite des liens vu qu'à la compilation, j'ai bien rajouter le paramètres : -c, comme vous me l'avez indiqué.
    Mais pourtant cette erreur, est bien-là :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    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)
    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
    #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:
    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
    #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:
    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 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*/
    Pourriez-vous m'éclairer à propos d’où elle peut venir cette fois-ci ?

    Et pour CMake, je suis en train de voir comment je peux l'utiliser avec VSCode (macOS) .

  4. #4
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Et, dis moi: Où elle est l'implémentation du constructeur de ta classe todo qui prend une std::string comme paramètre

    Car là, l'éditeur de liens se plaint de ne pas trouver le code binaire qui correspond à cette fonction, alors que tu y a fait appel dans la fonction main

    Et, en même temps, c'est normal, parce
    • que tu indique en toute lettre qu'un tel constructeur existe à lla ligne 14 du fichier main_functions.hpp.
    • tu y fais spécifiquement appel à la ligne 14 du fichier main.cpp
    • mais tu n'indique nulle part le code qui correspond à cette fonction pour que le compilateur soit en mesure de générer le code exécutable correspondant


    D'ailleurs, tu n'as pas non plus fourni l'implémentation du constructeur de todo qui ne prend aucun paramètre... Si l'éditeur de liens ne s'en plaint pas, c'est uniquement parce que tu n'y fait appel nulle part "par chance"

    Ah, et, au passage : il faut toujours veiller à n'inclure, dans un fichier -- quel qu'il soit -- que les fichiers d'en-tête dont tu as absolument besoin!

    Cela ne fait -- le plus souvent -- pas énormément de mal d'en rajouter qui sont inutiles, mais cela peut ralentir la compilation très rapidement

    Si bien que:
    • Le seul fichier d'en-tête dont tu aies réellement besoin dans main_functions.hpp, c'est <string> (pour que le compilateur sache que std::string existe)
    • En dehors de main_functions.hpp (pour que le compilateur sache que la classe todo existe), le seul fichier d'en-tête dont tu aies réellement besoin dans todo.cpp, c'est le fichier <fstream> pour pouvoir manipuler std::ifstream et std::ofstream, vu que main_functions.hpp inclut déjà <string>
    • En dehors de main_functions.hpp (pour que le compilateur sache que la classe todo existe), tu n'as besoin d'aucun autre fichier d'en-tête dans main.cpp, vu que la seule chose que tu utilises à part la classe todo, c'est la classe std::string, et que <string> est déjà inclut dans main_functions.hpp

    Tu ne verras sans doute aucune diminution sensible du temps de compilation sur ce projet particulier, mais tu connait le proverbe:
    Ce sont les petits ruisseaux qui produisent les gros fleuves
    Quand tu travailleras sur un projet de plusieurs centaines ou plusieurs milliers de fichiers, le temps gagné en n'incluant pas toute sortes de fichiers d'en-tête inutiles peut de faire gagner de précieuses minutes

  5. #5
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 483
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 483
    Points : 13 681
    Points
    13 681
    Billets dans le blog
    1
    Par défaut
    N'inclure que le minimum évite aussi les programmes qui compilent par chance.... et qui un jour ne compilent plus par malchance !

    Imagine que tu inclues Foo.hpp qui inclue Bar.hpp qui inclue <vector> alors que ni Foo.hpp ni Bar.hpp n'utilisent <vector>. Dans ton fichier cpp, tu utilises std::vector et ça compile parfaitement : ben oui, tu as inclus quelqu'un qui a inclus quelqu'un qui a inclus <vector> à ta place. Un jour, ces quelqu'uns font du ménage : ton code ne compile plus et tu te prends la tête.

  6. #6
    Membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2016
    Messages
    173
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Finistère (Bretagne)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2016
    Messages : 173
    Points : 50
    Points
    50
    Par défaut
    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.

    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
    63
    64
    65
    #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...

  7. #7
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Citation Envoyé par Rombutucraft Voir le message
    Merci beaucoup pour votre réponse

    Mais non, en fait l'objet n'était pas adapté à mon projet, en tout cas pour le moment.
    Ah, ca, c'est une autre paire de manches

    Disons que, dans un premier temps, il y avait tant à dire du point de vue technique qu'il n'était pas vraiment opportun de remettre une décision conceptuelle en cause. Même si elle est effectivement inopportune

    Je te félicite d'ailleurs d'avoir remis cette décision en cause par toi-même, car, de fait, l'approche orientée objet n'est absolument pas le "saint graal", et, pour ce que je comprends de ton projet, elle n'est effectivement pas adaptée du tout, et ne le sera sans doute jamais

    D'ailleurs, pour être clair : si je n'avais pas jugé opportun d'attirer ton attention sur différents points plus utiles (tels que comment compiler des projets multi fichiers, quels fichiers inclure à quel endroit et autre joyeusetés du genre), je ne me serais pas gêné pour te le faire remarquer

    Tout ce que j'espère, c'est que cette remise en cause n'est pas uniquement un moyen de contourner une difficulté à laquelle tu sera forcément confronté plus tard, à savoir : fournir une implémentation cohérente de différentes formes de constructeurs

    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...
    La raison est simple : chaque fois qu'il y a un espace entre deux mots / termes dans la commande d'exécution, le système d'exploitation considère qu'il s'agit d'un argument supplémentaire. Ainsi, si tu lances ton application sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    monAppli -a salut la compagnie
    argc vaudra 5 et les différentes chaines de caractères associées à argv vaudront respectivement
    • monAppli (argv[0])
    • -a (argv[1])
    • salut (argv[2])
    • la (argv[3])
    • compagnie (argv[4])

    L'une des solutions serait alors, "tout simplement" de regrouper les argument "qui n'ont rien à voir avec une instruction" sous la forme d'une collection dynamique. Par exemple, sous la forme d'un tableau de chaîne de caractères.

    Au passage, il serait peut-être opportun de respecter un peu plus scrupuleusement le SRP, ce qui nous amènerait à 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
    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
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
     
    /* Extrait chaque mot de la phrase à ajouter suite à l'instruction -a ou  -d
     * PRE: il y a au moins  un mot apprès cette instruction
     */
    std::vector<std::string> readSentence(int argc, char * argv){
        if(argc<=3){
            throw std::runtime_error("instruction should be followed with one word min");
        }
        std::vector<std::string> tab;
        for(int i=3; i<argc;++i){
            tab.emplace_back(argv[i]);
        }
        return tab;
    }
    /* on recrée la phrase, sur base de chaque mot */
    std::string createSentence(std::vector<std::string> const & words){
        size_t count{0};
        std::string sentence;
        for(auto const & it : words){
            sentence+= it;
            ++count;
            if(count <words.size())
                 sentence+=" ";
        }
        return sentence;
    }
    /* Par facilité, on pourrait créer une énumération permettant de représenter les instruction */
    enum Instructions{
        addToText,
        list,
        delFromText,
        MAXINSTRUCTIONS
    };
    /* Grâce à elle, nous pouvons analyser l'instruction qui est donnée sous une forme proche de */
    Instruction selectInstruction(std::string const & str){
        if(str == "-a")
            return addToText;
        if(str=="-d")
           return delFromText;
        if(str=="-ls")
          return list;
        return MAXINSTRUCTIONS;
    }
    /* on s'assure que le premier paramètre est "todo" */
    void checkFirstArg(std::string const & str){
         if(str!="todo")
            throw std::runtime_error("first argument should be ''todo''");
    }
    int main(int argc, char * argv[]){
        if(argc <3)
            throw std::runtime_error("Usage: todo <instruction> <paramètres éventuels>!");
        checkFirstArg(argv[1];
        auto instr = selectInstruction(argv[2]);
        std::string const fileName("./todo.txt");
        /* les énumération permettent l'utilisation de tests à choix multiples */
        switch(instr){
            case addToText:
                /* on récupère les mots */
               auto words= readSentence(argc, argv);
               auto recup = createSentence(words);
               add(fileName,  recup);
               break;
            case ls :
                list(fileName);
               break;
            case delFromText:
                auto toDel=createSentence(readSentence(argc, argv));
                del(fileName, toDel);
            default:
                assert(false, "you should never come here")
        }
        /* ... */
        return 0;
    }
    (Ah, au fait... L'affichage du contenu d'un fichier s'obtient au travers de l'instruction less : ls ne fait que lister les fichiers et les dossiers que l'on trouve dans un dossier. Autant garder les même termes pour obtenir les même résultats, non )

  8. #8
    Membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2016
    Messages
    173
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Finistère (Bretagne)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2016
    Messages : 173
    Points : 50
    Points
    50
    Par défaut
    Wouah, merci beaucoup pour ce code, que j'ai testé, malheureusement sans succès, et je sais pas si c'est volontaire de votre part de mettre quelques erreurs, par si, par là, en tout cas, je trouve ça pas mal pour mon apprentissage, du C++, j'en ai corrigé, plusieurs, mais malheureusement, une très grande partie me sont impossible, en tout cas pour le moment à résoudre .

    Voilà le code:
    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
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    #include <iostream>
    #include <string>
    #include <fstream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <vector>
     
    std::vector<std::string> readSentence(int argc, char * argv){
        if(argc<=3){
            throw std::runtime_error("instruction should be followed with one word min");
        }
        std::vector<std::string> tab;
        for(int i=3; i<argc;++i){
            tab.emplace_back(argv[i]);
        }
        return tab;
    }
    /* on recrée la phrase, sur base de chaque mot */
    std::string createSentence(std::vector<std::string> const & words){
        size_t count(0);
        std::string sentence;
        for(auto const & it : words){
            sentence+= it;
            ++count;
            if(count <words.size())
                 sentence+=" ";
        }
        return sentence;
    }
    /* Par facilité, on pourrait créer une énumération permettant de représenter les instruction */
    enum Instructions{
        addToText,
        list,
        delFromText,
        MAXINSTRUCTIONS
    };
    /* Grâce à elle, nous pouvons analyser l'instruction qui est donnée sous une forme proche de */
    Instructions selectInstruction(std::string const & str){
        if(str == "-a")
            return Instructions::addToText;
        if(str=="-d")
           return Instructions::delFromText;
        if(str=="-ls")
          return Instructions::list;
        return Instructions::MAXINSTRUCTIONS;
    }
    /* on s'assure que le premier paramètre est "todo" */
    void checkFirstArg(std::string const & str){
         if(str!="todo")
            throw std::runtime_error("first argument should be ''todo''");
    }
     
    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 less(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, char * argv[]){
        if(argc <3)
            throw std::runtime_error("Usage: todo <instruction> <paramètres éventuels>!");
        checkFirstArg(argv[1]);
        auto instr = selectInstruction(argv[2]);
        std::string const fileName("./todo.txt");
        /* les énumération permettent l'utilisation de tests à choix multiples */
        switch(instr){
            case addToText:
                /* on récupère les mots */
               auto words = readSentence(argc, argv);
               auto recup = createSentence(words);
               add(fileName, recup);
               break;
            case ls :
                less(fileName);
               break;
            case delFromText:
                auto toDel = createSentence(readSentence(argc, argv));
                del(fileName, toDel);
            default:
                assert(false, "you should never come here");
        }
        /* ... */
        return 0;
    }
    Erreurs:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    	no member named 'emplace_back' in 'std::__1::vector<std::__1::basic_string<char>, std::__1::allocator<std::__1::basic_string<char> > >'
    transfer of control bypasses initialization of: -- variable "words" (declared at line 119) -- variable "recup" (declared at line 120) -- variable "toDel" (declared at line 127)
    no matching function for call to 'readSentence'
    argument of type "char **" is incompatible with parameter of type "char *"
    use of undeclared identifier 'ls'
    identifier "ls" is undefined
    no matching function for call to 'readSentence'
    argument of type "char **" is incompatible with parameter of type "char *"
    no suitable conversion function from "std::__cxx11::string" to "int" exists
    use of undeclared identifier 'assert'
    identifier "assert" is undefined
    too many arguments provided to function-like macro invocation
    }
    Pourtant dans ces erreurs, je comprends que certaines variables comme "ls" ne semble pas être déclarée, mais c'est faux, donc peut-être qu'elle est inaccessible ?
    Mais pour beaucoup de ces erreurs, je me retrouve coincé

    C'est hyper cool, en vrai de réussir à corriger certaines erreurs, mais ce qui l'est moins, c'est d'avoir son code en suspend, car trop complexe à résoudre, pour le moment

    En tout cas un grand merci à vous

  9. #9
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Au temps pour moi... je n'avais pas fait attention au prototype de la fonction del...

    Et non, les erreurs n'étaient pas voulues... j'étais fatigué, et je n'ai pas pris le temps de tester mon code... j'ai donc laissé passé quelques erreurs suite à des changement d'idées


    Rajoutons donc une petite fonction pour extraire le numéro de ligne que tu souhaites supprimer de la ligne de commande. Elle prendrait la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    int extractLineNumber(std::string const & str){
    	size_t position{0};
    	int result = std::stoi(str, &position);
    	if(position!= str.size())
    		throw std::runtime_error("unable to extract line numbeer from command line");
    	return result;
    }
    Profites en pour virer ces odieuses inclusions de stdlib.h et stdio.h, qui n'ont absolument rien à faire ici! et pour corriger le prototype de readSentence (j'étais un peu fatigué, et je n'avais pas testé mon code ) pour lui donner la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::vector<std::string> readSentence(int argc, char * argv[])
    (ben oui, il faut lui transmettre l'ensemble des paramètres de la lignes de commande )


    Par contre, tu peux ajouter l'inclusion de <cassert> pour pouvoir profiter de la macro assert

    On va aussi corriger la fonction d'extraction des mots, pour éviter qu'elle ne lance une exception lorsque le seul paramètre est -ls
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    std::vector<std::string> readSentence(int argc, char * argv[]){
        std::vector<std::string> tab;
        for(int i=3; i<argc;++i){
            tab.emplace_back(argv[i]);
        }
        return tab;
    }
    Et on va terminer en corrigeant le code de la fonction main pour lui donner la forme de
    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
    int main(int argc, char * argv[]){
        if(argc <3)
            throw std::runtime_error("Usage: todo <instruction> <paramètres éventuels>!");
        checkFirstArg(argv[1]);
        auto instr = selectInstruction(argv[2]);
        std::string const fileName("./todo.txt");
        /* récupération de la phrase (s'il y en a une)
         * parce que le compilateur n'aime pas trop que l'on déclare une variable dans les switch ;) 
         */
       auto words = readSentence(argc, argv);
       auto recup = createSentence(words);
        switch(instr){
            case addToText:
                /* on récupère les mots */
               add(fileName, recup);
               break;
            case list :
                less(fileName);
               break;
            case delFromText:
                del(fileName, extractLineNumber(argv[3]));
            default:
                /* corrigeons l'assertion car je ne sait pas d'où vient cette virgule... j'étais vraiment fatigué :D */
                assert(false && "you should never come here");
        }
        /* ... */
        return 0;
    }
    Par contre, il faudra corriger ta fonction add, car un test rapide de trois exécutions du programme, sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    ./a todo -a salut la compagnie
    ./a todo -a helllo world
    ./a todo -ls
    ne m'affiche que "hello world"...

    Une petite remarque concernant la classe std::ofstream : C'est forcément un fichier de sortie, il n'y a donc absolument aucune raison de rajouter le flag std::ios::outPar contre, tu souhaites sans doute que ton programme ajoute les nouvelles phrases à la fin du fichier, sans supprimer les phrases qui s'y trouvent avant... Tu devrais donc utiliser le flag std::ios::app

  10. #10
    Membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2016
    Messages
    173
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Finistère (Bretagne)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2016
    Messages : 173
    Points : 50
    Points
    50
    Par défaut
    Ha mince, j'avais pas vu ce bug, je vais voir ce que je peux en faire, en tout cas merci beaucoup, car la majorité, des fonctions du programmes, fonctionnent parfaitement
    Pour info j'ai mis le projet sur GitHub. https://github.com/romainOS/todo-CLI
    je préciserai, que tu m'as aider, à réaliser le code, parce qu'il faut l'avouer, sans toi je serais coincé

    Bonne journée à toi

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Erreur de compilation => Erreur de débutant
    Par TonyRc dans le forum VBA Access
    Réponses: 0
    Dernier message: 27/05/2008, 12h01
  2. Réponses: 2
    Dernier message: 09/12/2006, 14h42
  3. [Débutant] [Compilation] Avertissement deprecated
    Par javamantools dans le forum Langage
    Réponses: 2
    Dernier message: 08/07/2005, 15h33
  4. [Débutant]Compilation d'un fichier .java
    Par adilou1981 dans le forum Eclipse Java
    Réponses: 2
    Dernier message: 15/04/2005, 14h46
  5. Erreurs à la compilation
    Par Code source dans le forum GLUT
    Réponses: 11
    Dernier message: 02/05/2004, 19h33

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