Pfff le C/C++ c'est dur

Non non ce n’est pas de l’humour à deux sous, j’ai un problème pour utiliser les fonctions exec*.

L’idée c’est que j’ai un exo où on me demande de réécrire la fonction system(), utilisant une fonction de la familles des exec*. C’est intéressant mais je m’en sort pas. Je tiens à dire pour ceux qui aurais un doute que j’ai la correction du prof, donc ce n’est en aucun cas de la triche, je trouve juste sa solution pas génial.

Donc après mettre pris la tête avec des allocation dynamiques de mémoire pour aggrandir un tableau j’ai utilisé une string est un vector. Maintenant faut que j’arrive à correctement “transformer” mon vecteur de string en tableau de pointeurs de ntcts.

Voici ma fonction :

[code]namespace
{
typedef vector VStr;
typedef VStr::iterator ItVStr;

int System (const char * const Commande)
{
    const int CmdSz = strlen(Commande);
    VStr Argv;
    bool Protect = false;
    for (int i (0); i < CmdSz;)
    {
        string Str;
        for(; i < CmdSz; ++i)
        {
            if (false == Protect && ' ' == Commande[i])
            {
                ++i;
                break;
            }

            if ('\'' == Commande[i] && '"' == Commande[i])
                Protect = !Protect;

            Str.push_back(Commande[i]);
        }
        if (!Str.empty())
            Argv.push_back(Str);
    }
    cerr << "Taille de Argv : " << Argv.size() << endl;
    char * args [Argv.size() + 1];
    for (size_t i (0); i < Argv.size(); ++i)
    {
        //cout << Argv[i] << endl;
        strcpy(args[i], Argv[i].c_str());
        //cout << args[i] << endl;
    }

    args[Argv.size()] = 0;//static_cast<char*> (0);

    for (size_t i (0); i < Argv.size(); ++i)
        cout << args[i] << endl;

    return execv(args[0], args);
} // System()

} // namespace[/code]
Quand je le lance avec “ls -l exo_04_2.cxx” ça fonctionne mais pas avec “ls exo_04_2.cxx” ni quand j’augmente le nombre d’arguments.
Je me retrouve soit avec une segfault soit avec un

Et franchement je vois pas pourquoi.

déjà, ton code, c’est pas du C, ou alors, c’est un dialecte trés proche du C++. :wink:
Sinon, aucune idée d’ou ça vient.

Pas faut mais les problèmes de char ** c’est du C.
Ce serais si bien un noyo en C++ :laughing: (je sens que je vais me faire des amis)

je confirme c’est du C++ et étant donné que le C++ est une couche d’abstraction objet sur du C (enfin pour faire simple) normal qu’il y ai des similitudes avec du C :wink:

Pour commencer les bases commences par du C pur et dur et essayant de maitriser les bases , les pointeurs surtout .
ensuite quand tu aura des connaissances suffisantes en C là tu pourra commencer le C++.

beurk, du C++ pris pour du C…

[quote=“mrpouet”]Pour commencer les bases commences par du C pur et dur et essayant de maitriser les bases , les pointeurs surtout .
ensuite quand tu aura des connaissances suffisantes en C là tu pourra commencer le C++.[/quote]
Non mais en faite ça fais 1 ans que j’ai commencé à apprendre à programmer et mes prof ont construit leur cours en l’axant sur le C++ (mais pas pur et dure c’est à dire avec pointeurs, structures, macro-commandes,…).
Par contre la familles des alloc et exec je les apprends en ce moment (vu que c’est du système et pas de l’algo).

Désolé d’avoir blasphémé sur les sacro saintes règles définissant le C et le C++, mais perso le sectarisme c’est pas mon truc.

Merci bien je me débrouillerais donc.

quote="MisterFreez"
Désolé d’avoir blasphémé sur les sacro saintes règles définissant le C et le C++, mais perso le sectarisme c’est pas mon truc.
(…)[/quote]Notes bien que je ne faisais personnellement aucun reproche, juste une remarque.

Maintenant, le problême avec les objets que tu manipules comme les vecteurs, ou les iterateurs, c’est que ça te masque des allocations, et de manière général les effets des constructeurs.
En utilisant des types C élémentaires et en gèrant tout toi même, tu as le controle sur la position réelle des chaines que tu manipules ou que tu alloues.
J’en ai bouffé du C++, mais je peux te dire qu’ici avec le temps qui a passé sur ma connaissance des classe genre iterateur, et autres, je n’arrive pas du tout à voir ce qui se passe dans ton code en terme de position de tes objets en mêmoire.
C’est pour ça que mrpouet a raison, tu dois penser d’abord en C en imaginant comment sont tes objets en mêmoire, même si aprés, tu écris la même chose en C++, avec les simplifications d’écriture que ça peut apporter.
Penser en C++ necessite AMA une maîtrise du C, parceque ce que ça apporte, c’est surtout du raccourcissement du code source, donc une plus grande opacité du même code.

Mais j’aimais beaucoup le C++ que le C, là n’est pas la question.

Ton raisonnement reste toute fois récursif oblige a aller jusqu’à la connaissance au niveau physique. :laughing:

J’ai en effet comme dit plus haut commencer à écrire la chose avec des variable de type C basique et des pointeurs. La complexité due à la réalocation de mémoire pour chaque itération de ma “grande boucle”, m’a conduit à utiliser un vecteur en pensant ensuite que la gestion d’un tableau de pointeur de ntcts serais plus simple vu que je connais les dimensions de celui - ci. J’ajouterais que m’amuser à déclarer un vector pour ensuite déclarer un tableau de ntcts ne me plaît pas plus que ça mais pour le moment c’est ce qui m’a parut le plus simple, l’optimisation viendras dans un second temps.

Je suis désolé si mon code n’est pas claire.

A oui dernière chose tu remarqueras que t’ayant répondu un fois avant mon dernier post ce dernier ne te concernait pas.

quote="MisterFreez"
A oui dernière chose tu remarqueras que t’ayant répondu un fois avant mon dernier post ce dernier ne te concernait pas.[/quote]Je prenais la défense du pouet. Il faut défendre les pouets: ils sont sensibles, tout le monde sait ça. :laughing:

Pas encore fait de C++ mais la ou je rejoins ce qui s’est dit c’est qu’il vaudrait mieux commencer en C “dur” pour savoir ou tu vas (pourquoi pas même en pseudo-code, des fois ça aide, avant de t’attaquer à la “traduction” en C++.
En C je pense que je peux dire sans trop m’avancer que le segfault en manipulant des chaines de caractères est à 90% du à un défaut d’allocation, ou une mauvaise allocation, de ces dernières, et en particulier quand on les manipule dans des tableaux. Alors je ne sais pas à quel point le C++ te mâche le travail au niveau alloc mais tu ferais peut être bien de regarder par la ^^ et pis le strcpy qui te copie pas tout quand t’as qu’un seul paramètre c’est étrange aussi m’enfin bon …

quote="Hoshin"
et pis le strcpy qui te copie pas tout quand t’as qu’un seul paramètre c’est étrange aussi m’enfin bon …[/quote]Un oubli de \0 ?

Pour information, le premier argument d’une commande est toujours le nom de la commande elle même, donc :

passera mieux que :

Mais je dis ça, je dis rien :unamused: :unamused:

++

certainement.

Et sinon pour le comportement et les paramètres des primitives exec : man exec :stuck_out_tongue:

Edit : Ah et sinon

for(; i < CmdSz; ++i) 

====> pouark ! je veux bien que la prog ca soit une discipline de feignant (et que c’est en partie pour ca que je l’étudie en niveau master … le maitre des feignants ca sera moi et personne d’autre !) mais quand même XD (et t’es certain de devoir faire une pré-incrémentation et pas une post ?). Perso je pense que si tu mets de l’ordre dans tes boucles en faisant un truc un chouilla plus propre en saisissant un peu mieux comment tout cela se passe ca ira un peu mieux.

Edit2 :
et avec execlp faut pas terminer la liste d’arguments par NULL ? ou jme gourre complètement ? De plus comme tu sais la taille du “argv” que tu vas passer à ton exec tu peux utiliser execvp plutot que lp.

[quote=“Hoshin”]Edit2 :
et avec execlp faut pas terminer la liste d’arguments par NULL ? ou jme gourre complètement ? De plus comme tu sais la taille du “argv” que tu vas passer à ton exec tu peux utiliser execvp plutot que lp.[/quote]

+1 pour les deux points, non seulement faut mettre le premier argument comme non de commande, mais en plus faut terminer parun arg NULL :wink:

++

c’est ce que je disais: \0

[quote=“mattotop”]quote="Hoshin"
et pis le strcpy qui te copie pas tout quand t’as qu’un seul paramètre c’est étrange aussi m’enfin bon …[/quote]
Un oubli de \0 ?[/quote]
Les string sont des pointeurs vers des ntcts donc les chaines finissent par un \0 (je vais tout de même tenté d’ajouter un \0).

[quote=“buchs”]Pour information, le premier argument d’une commande est toujours le nom de la commande elle même, donc :

C’est incohérent avec le profile de execlp.

[quote=“buchs”]passera mieux que :

Mais je dis ça, je dis rien :unamused: :unamused:

++[/quote]
Args contiens toute ma commande nom du programme compris, donc args[0] c’est le nom du programme et je luis passe en arguments tout les arguments non du prog compris.

[quote=“Hoshin”]Edit : Ah et sinon

for(; i < CmdSz; ++i) 

====> pouark ! je veux bien que la prog ca soit une discipline de feignant (et que c’est en partie pour ca que je l’étudie en niveau master … le maitre des feignants ca sera moi et personne d’autre !) mais quand même XD (et t’es certain de devoir faire une pré-incrémentation et pas une post ?). Perso je pense que si tu mets de l’ordre dans tes boucles en faisant un truc un chouilla plus propre en saisissant un peu mieux comment tout cela se passe ca ira un peu mieux.[/quote]
Je ne crois pas que tu réfléchisse de la bonne façon au sujet de la post/près incrémentation. Par défaut j’utilise un près et je ne me sert de la post uniquement si je me sert de la valeur de retour. La post incrémentation renvoie un copie de la variable alors que la près incrémentation c’est juste une incrémentation basique.

[quote=“Hoshin”]Edit2 :
et avec execlp faut pas terminer la liste d’arguments par NULL ? ou jme gourre complètement ? De plus comme tu sais la taille du “argv” que tu vas passer à ton exec tu peux utiliser execvp plutot que lp.[/quote]
J’ai jamais utilisé execlp, il me faut je pense execvp car system utilise le shell et donc le PATH qui lui est associé.

[quote=“buchs”][quote=“Hoshin”]Edit2 :
et avec execlp faut pas terminer la liste d’arguments par NULL ? ou jme gourre complètement ? De plus comme tu sais la taille du “argv” que tu vas passer à ton exec tu peux utiliser execvp plutot que lp.[/quote]

+1 pour les deux points, non seulement faut mettre le premier argument comme non de commande, mais en plus faut terminer parun arg NULL :wink: [/quote]
C’est pas ce que je fais avec :

ou

ou

J’ai essayé les trois.

J’ai résolue une bonne partie du problème en faisant une allocation manuelle de la mémoire avant le strcpy. Je pensais qu’elle le faisait elle même.

Mon dernier problème c’est que quand je lui passe une commande avec un fichier en argument il ajoute un q à la fin du nom de fichier et ne le trouve donc pas.

Donc execlp est une focntion de la lib standart qui prend en paramètres un nombre arbitraire d’arguments, après je ne sais pas si le char ** TesArgs est “vu” de la même facon qu’une liste “explicite” (arg1, arg2, …, NULL)

arg[0] contient effectivement le nom du programme lancé et fait partie des arguments, cela sert en particulier pour lancer les shells en mode “login” ou en mode simple en comparant args[0] et args[1].

Etant en master d’info j’ai une vague idée de ce que je raconte sur les pré/post incrémentations et les parcours de tableaux en général, je te dis juste que ta méthode n’est pas forcément la plus orthodoxe pour arriver à tes fins et qu’ommettre des éléments dans ton code ne fait que le rendre plus illisible : "for(; i < CmdSz; ++i) " ca s’écrit “pour aller vite parceque bon a saoule ces trucs tous simples”, mais quand quelque chose bloque il ne faut souvent pas hésiter à revenir en arrière et à tout réécrire proprement.

execlp et execvp utilisent tous les deux la variable d’environnement PATH, mais le premier utilise une liste d’arguments et l’autre un vecteur (tableau).

Concernant les ajouts en fin de chaine c’est très certainement que les chaines de caractères dans lesquelles tu copies ne sont pas allouées correctement ou que, à cause d’un parcours que je ne m’explique pas de ta liste d’arguments, tu effectues plusieurs fois une copie sur le même emplacement de ton tableau d’arguments. (enfin ca se peut … c’est à vérivier quoi).

Voili voila voilo en espérant que ca te sera utile.

Si je n’ai pas utilisé tout les champs ddes deux for c’était pour économiser une variable et pas pour un souci de vitesse d’écriture.
Voici mon nouveau code :

[code]typedef vector VStr;
typedef VStr::iterator ItVStr;

int System (const char * const Commande)
{
const int CmdSz = strlen(Commande);
VStr Argv;
//bool Protect (false);

for (int i (0), j; i < CmdSz; i = j)
{
    string Str;
    for(j = i; j < CmdSz; ++j)
    {
        if (/*false == Protect &&*/ ' ' == Commande[j])
        {
            ++j;
            break;
        }

        Str.push_back(Commande[j]);
    }
    if (!Str.empty())
        Argv.push_back(Str);
 }

char ** args = (char **) malloc ((Argv.size() + 1) * sizeof (char*));

for (size_t i (0); i < Argv.size(); ++i)
{
    args[i] = (char *) malloc (Argv.size() * sizeof (char));
    strcpy(args[i], Argv[i].c_str());
    cerr << args[i] << endl;
}

args[Argv.size()] = (char *) 0;

return execvp(args[0], args);

} // System()[/code]

Je n’ai plus que le souci du caractère en plus au dernier fichier passé en arguments que je ne comprends pas. Je vais tenter de ne plus utiliser de vecteor ni de string et voir ce que ça donne.

en C “basique” avec une bonne vieille boucle “do{…}while(…);” tu dois pouvoir t’en sortir, essayes de voir si tu ne peux pas au moins te passer d’une de tes “for” pour éclaircir un peu tout ca. Et sinon n’oublie pas l’adage du programmeur : “If you need more than 3 levels of indentation, you’re screwed anyway.”, autrement dit, et en français dans le texte : “Passés trois niveaux d’indentation c’est même pas la peine de continuer”. C’est bien sur faux sur les “gros” programmes, mais sur les petites fonctions c’est assez vrai (tout simplement aprcequ’on finit par s’embrouiller soi même a trop vouloir multiplier les niveaux.
Essayes de voir “sur papier” comment tu pourrais faire pour alléger tout ca. Pour ca je te conseille de complètement abstraire le code, écris en pseudo code et tu feras la trado en c++ quand ca sera au point.

J’aime bien ma combinaisons de boucle parce qu’elle est sémantique, la première pour traiter l’ensemble de la commande et la seconde pour traiter argument par argument. Si j’en vire une le code deviens plus brouillons je pense et moins lisible. Je vais tout de même essayer, sinon je crée une fonction supplémentaire (c’est souvent ce qu’il est sous entendu par les règles d’indentations et de taille du code (70 * 80 caractères max), sachant qu’en C++ l’usage des namespace contraint généralement à avoir une indentation de nom supplémentaire). D’ailleurs en y réfléchissant passer par des fonctions supplémentaire me semble être une bonne idée. :wink: