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 :

Comment empêcher un SEGFAULT sur une fonction à arguments variables ?


Sujet :

Langage C++

  1. #1
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2014
    Messages
    26
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2014
    Messages : 26
    Points : 11
    Points
    11
    Par défaut Comment empêcher un SEGFAULT sur une fonction à arguments variables ?
    Bonjour,

    Voilà le code en question :

    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
     
    #include <iostream>
    #include <string>
    #include <cstdarg>
    #include <csignal>
    #include <unistd.h>
    #include <cstring>
     
    void tracer(const char *fmt, ...)
    {
        va_list args;
        va_start(args, fmt);
     
        while (*fmt != '\0')  {
            if (*fmt == 'd') {
                int i = va_arg(args, int);
                std::cout << i << " ";
            } else if (*fmt == 's') {
                char * s = va_arg(args, char*);
            std::cout << s << " ";           
            }
            ++fmt;
        }
        std::cout << std::endl;
     
        va_end(args);
    }
     
    int main()
    {
        std::string str1( "Hello" ), str2( "world" );
     
        int k=10;
     
        // cas 1   
        //tracer("sds", str1.c_str(), 1, str2.c_str());  // OK 
        // cas 2
        //tracer("sds", str1.c_str(), k, str2.c_str(), 2, "ça marche");  // OK avec plus d'arguments que nécessaire
        // cas 3
        //tracer("sdsd", str1.c_str(), 3, str2.c_str(), "pas de SEGFAULT mais pas de sens");  // OK "pas de SEGFAULT mais pas de sens" affiche 4198152
        // cas 4
        //tracer("sdsdd", str1.c_str(), k, str2.c_str(), 5);  // OK la valeur de l'entier du dernier argument est aléatoire 
        // cas 5
        //tracer("sdss", str1.c_str(), k, str2.c_str(), 4);  // SEGFAULT : 4 n'est pas une chaîne  
        // cas 6
        tracer("sdsds", str1.c_str(), k, str2.c_str(), 5);  // SEGFAULT : manque une chaine dans les paramètres
     
        return 0 ;
    }
    Ainsi mon problème c'est que je n'arrive pas à trouver de moyen qui puisse m'éviter d'obtenir un SEGFAULT, si je ne rentre pas le bon format (cas 5) ou si j'oublie une chaîne dans les paramètres (cas 6).

    Y aurait-t-il donc un moyen d'éviter ces SEGFAULT ?

    Merci

  2. #2
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    À partir du C++11, tu peux faire une fonction template variadique à la place du ... classique.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  3. #3
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 186
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 186
    Points : 17 126
    Points
    17 126
    Par défaut
    Sinon, tu dois pouvoir vérifier le nombre d'argument, ou qu'il y en a assez.

    à tout hasard, comment se comporte printf dans le même cas?
    printf("%d%d",1); provoque-t-il une segfault?
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  4. #4
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Citation Envoyé par leternel Voir le message
    à tout hasard, comment se comporte printf dans le même cas?
    printf("%d%d",1); provoque-t-il une segfault?
    C'est un comportement indéfini.
    GCC émet des warnings si le format et le type son incohérent.

    Note "amusante" : Si on inverse deux formats, GCC se permet d'inverser lui-même
    Exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // gcc -Wall -Wextra -Wconversion -Wsign-conversion -std=c99 -pedantic -fopenmp main.c -o main_c && ./main_c
    // gcc -Wall -Wextra -Wconversion -Wsign-conversion -std=c99 -pedantic main.c -o main_c && valgrind ./main_c
     
    #include <stdio.h>
     
     
    int main()
    {
    	printf("int = %f | float = %d\n", 42, 73.21);
    	return 0;
    }
    Compilation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    main.c: In function ‘main’:
    main.c:9:2: warning: format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘int[-Wformat=]
      printf("int = %f | float = %d\n", 42, 73.21);
      ^
    main.c:9:2: warning: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘double[-Wformat=]
    Exécution :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    int = 73.210000 | float = 42

  5. #5
    Membre éprouvé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2014
    Messages
    345
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Juin 2014
    Messages : 345
    Points : 1 211
    Points
    1 211
    Par défaut
    Au passage, sur GCC il y a __attribute__((format(printf, ...))) qui permet d'imiter le comportement de printf vis-à-vis des warnings à la compilation.

  6. #6
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Citation Envoyé par Ehonn
    Note "amusante" : Si on inverse deux formats, GCC se permet d'inverser lui-même
    Sous quelle plate-forme exactement? x86? x86-64? Peux-tu poster le code assembleur obtenu?
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  7. #7
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    uname -a
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Linux Korsusa 3.14-2-amd64 #1 SMP Debian 3.14.15-2 (2014-08-09) x86_64 GNU/Linux
    gcc -v
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Using built-in specs.
    COLLECT_GCC=gcc
    COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.9/lto-wrapper
    Target: x86_64-linux-gnu
    Configured with: ../src/configure -v --with-pkgversion='Debian 4.9.1-11' --with-bugurl=file:///usr/share/doc/gcc-4.9/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.9 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.9 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.9-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.9-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.9-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --with-arch-32=i586 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
    Thread model: posix
    gcc version 4.9.1 (Debian 4.9.1-11)
    cat /proc/cpuinfo | grep "model name" | head -n1
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    model name      : Intel(R) Core(TM)2 Quad  CPU   Q9000  @ 2.00GHz
    gcc -Wall -Wextra -Wconversion -Wsign-conversion -std=c99 -pedantic -S main.c && cat main.s
    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
            .file   "main.c"
            .section        .rodata
    .LC1:
            .string "int = %f | float = %d\n"
            .text
            .globl  main
            .type   main, @function
    main:
    .LFB0:
            .cfi_startproc
            pushq   %rbp
            .cfi_def_cfa_offset 16
            .cfi_offset 6, -16
            movq    %rsp, %rbp
            .cfi_def_cfa_register 6
            subq    $16, %rsp
            movabsq $4634852112698116669, %rax
            movq    %rax, -8(%rbp)
            movsd   -8(%rbp), %xmm0
            movl    $42, %esi
            movl    $.LC1, %edi
            movl    $1, %eax
            call    printf
            movl    $0, %eax
            leave
            .cfi_def_cfa 7, 8
            ret
            .cfi_endproc
    .LFE0:
            .size   main, .-main
            .ident  "GCC: (Debian 4.9.1-11) 4.9.1"
            .section        .note.GNU-stack,"",@progbits

  8. #8
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    C'est bien ce qu'il me semblait, ce n'est pas une histoire d'inversion, mais une histoire de registres flottants vs entiers (ESI vs XMM0).

    Je suis surpris, il me semblait que les registres n'étaient justement pas utilisés pour les fonctions variadiques. J'ai du confondre avec la convention d'appel 64 bits sous Windows...

    Edit: Et si printf("%x", unFlottant) est un comportement indéfini, alors ce que fait gcc n'est pas un bug.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  9. #9
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2014
    Messages
    26
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2014
    Messages : 26
    Points : 11
    Points
    11
    Par défaut
    Citation Envoyé par leternel Voir le message
    Sinon, tu dois pouvoir vérifier le nombre d'argument, ou qu'il y en a assez.
    Je peux compter le nombre d'arguments à partir du format. Mais comment faire pour compter le nombre de paramètres que je passe réellement à la fonction ? Et ainsi vérifier s'il correspond bien au format donné en premier argument.
    Ce qui pose un problème notamment dans le cas 6 où le nombre de paramètre lors de l'appel de tracer() est inférieur au nombre d'arguments prévu par le format.

    Et justement sur le modèle de printf qui permet de faire ou sans segfault, n'y aurait-il pas un moyen simple de faire la même chose. Ma fonction tracer() est en fait un printf low-cost mais j'aimerais tout de même la protéger contre les segfault.

  10. #10
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Comme je le disais, en C++11 tu dois pouvoir vérifier cela avec une fonction template variadique; en C++03 tu dois pouvoir le simuler avec des surcharges de fonction templates, jusqu'à une certaine limite au nombre d'arguments (8 peut être raisonnable).
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  11. #11
    Membre éclairé

    Homme Profil pro
    Non disponible
    Inscrit en
    Décembre 2012
    Messages
    478
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Non disponible

    Informations forums :
    Inscription : Décembre 2012
    Messages : 478
    Points : 877
    Points
    877
    Billets dans le blog
    1
    Par défaut
    Bonjour,

    comme l'a dit Médinoc, les variadiques templates serait préferable, à défaut de connaitre l'utilisation concrete de cette fonction voici un exemple :
    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
    //==============================================================================
    #include <iostream>
    //------------------------------------------------------------------------------
    //Si seulement 1 argument
    template<typename Type>
    void draw(const Type& type)
    {
        std::cout << type << std::endl;
    }
    //Plusieurs arguments
    template<typename Type, typename... Tail>
    void draw(const Type& type, const Tail&... tail)
    {
        std::cout << sizeof...(Tail) << " ";//Compte le nombre d'arguments variadique
        static_assert(sizeof...(Tail) <= 4, "Trop d'arguments");//Tester à la compilation le nombre d'arguments
        std::cout << type << std::endl;
        draw(tail...);//Imprime le reste (fonction recursive)
    }
    //==============================================================================
    int main(int, char**)
    {
        draw("arg", 5, "bla", 85, 4);//ok
        draw("arg", 5, "bla", 85, 4, "arg_de_trop");//Erreur
        return 0;
    }
    //==============================================================================

  12. #12
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2014
    Messages
    26
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2014
    Messages : 26
    Points : 11
    Points
    11
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Comme je le disais, en C++11 tu dois pouvoir vérifier cela avec une fonction template variadique; en C++03 tu dois pouvoir le simuler avec des surcharges de fonction templates, jusqu'à une certaine limite au nombre d'arguments (8 peut être raisonnable).
    Etant limité au C++03 j'ai utilisé les surcharges de fonctions templates de cette façon :

    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
     
    template<typename Type1>
    void tracer1(const char * fmt, Type1 type1)
    {
    	if(strlen(fmt) <= 1)
    		tracer(fmt, type1);
    	else
    		cout << "Trop d'arguments !" << endl; 
    }
     
    ...
     
    template<typename Type1, typename Type2, typename Type3, typename Type4, typename Type5, typename Type6, typename Type7, typename Type8, typename Type9>
    void tracer9(const char * fmt, Type1 type1, Type2 type2, Type3 type3, Type4 type4, Type5 type5, Type6 type6, Type7 type7, Type8 type8, Type9 type9)
    {
    	if(strlen(fmt) <= 9)
    		tracer(fmt, type1, type2, type3, type4, type5, type6, type7, type8, type9);
    	else
    		cout << "Trop d'arguments !" << endl; 
    }
    ça résout donc bien le problème du cas 6 (cf. 1er post) où j'ai moins de paramètres dans l'appel de tracer() que d'arguments prévu par le format.

    Cependant, ça ne résout pas le segfault du cas 5 où je passe en paramètres un int au lieu d'un char* prévu par le format.

    Une idée ?

  13. #13
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Déjà, il te faudra quelque chose de mieux que strlen, mais ce sera un truc du genre:
    Code C++ : 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
    //Fonction à mettre quelque part en dur
    char getPercentCode(char const *fmt, size_t &offset)
    {
    	char const *pPercent = strchr(fmt+offset, '%');
    	if(pPercent==NULL)
    		return '\0'; //Erreur, trop d'arguments
    	char c;
    	for(offset =(fmt-pPercent)+1 ; fmt[offset++] != '\0' ; )
    	{
    		switch(c)
    		{
    		case '\0': //Pourcent solitaire, c'est une erreur.
    		case 'f':
    		case 'd':
    		case 'u':
    		case 's':
    			return c;
    		case 'x':
    			return 'u';
    		//etc.
    		//Note: Sur une plate-forme 64-bits, il faudrait aussi gérer les préfixes de longueur,
    		//et peut-être retourner une lettre majuscule pour les long / long long...
    		case '%':
    			//Cas spécial, il faut recommencer l'analyse
    			//car ce n'était pas un champ correspondant à un argument:
    			pPercent = strchr(fmt+offset, '%');
    			if(pPercent==NULL)
    				return '\0'; //Erreur, trop d'arguments
    			offset = (fmt-pPercent)+1;
    		}
    	}
    	offset = strlen(fmt); //Pour être sûr de ne pas pointer derrière le caractère nul terminal.
    	return '\0';
    }
     
    template< class T >
    struct FormatForType { };
     
    template<> struct FormatForType<int> { static const char fmt='d'; };
    template<> struct FormatForType<unsigned int> { static const char fmt='u'; };
    template<> struct FormatForType<double> { static const char fmt='f'; }
    template<> struct FormatForType<std::string> { static const char fmt='s'; }
    template<> struct FormatForType<char const*> { static const char fmt='s'; }
    template<> struct FormatForType<char*> { static const char fmt='s'; }
    //etc.
     
    void throwFormatError(int argNum, char inFormat, char inType);
     
    template<typename Type1, typename Type2, typename Type3, typename Type4, typename Type5, typename Type6, typename Type7, typename Type8, typename Type9>
    void tracer9(const char * fmt, Type1 type1, Type2 type2, Type3 type3, Type4 type4, Type5 type5, Type6 type6, Type7 type7, Type8 type8, Type9 type9)
    {
    	char code;
    	size_t offset = 0;
    	if((code=getPercentCode(fmt, offset)) != FormatForType<Type1>::fmt) { throwFormatError(1, code, FormatForType<Type1>::fmt); }
    	if((code=getPercentCode(fmt, offset)) != FormatForType<Type2>::fmt) { throwFormatError(2, code, FormatForType<Type2>::fmt); }
    	if((code=getPercentCode(fmt, offset)) != FormatForType<Type3>::fmt) { throwFormatError(3, code, FormatForType<Type3>::fmt); }
    	if((code=getPercentCode(fmt, offset)) != FormatForType<Type4>::fmt) { throwFormatError(4, code, FormatForType<Type4>::fmt); }
    	if((code=getPercentCode(fmt, offset)) != FormatForType<Type5>::fmt) { throwFormatError(5, code, FormatForType<Type5>::fmt); }
    	if((code=getPercentCode(fmt, offset)) != FormatForType<Type6>::fmt) { throwFormatError(6, code, FormatForType<Type6>::fmt); }
    	if((code=getPercentCode(fmt, offset)) != FormatForType<Type7>::fmt) { throwFormatError(7, code, FormatForType<Type7>::fmt); }
    	if((code=getPercentCode(fmt, offset)) != FormatForType<Type8>::fmt) { throwFormatError(8, code, FormatForType<Type8>::fmt); }
    	if((code=getPercentCode(fmt, offset)) != FormatForType<Type9>::fmt) { throwFormatError(9, code, FormatForType<Type9>::fmt); }
     
    	if(strchr(fmt+offset, '%') != NULL)
    		throw std::invalid_argument("Il y a plus de pourcents dans le format que d'arguments passés à la fonction!");
     
    	tracer(fmt, type1, type2, type3, type4, type5, type6, type7, type8, type9);
    }
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  14. #14
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Et cela peut encore être amélioré. Déjà en dissociant les formats des codes de type:
    Code C++ : 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
    //Fonction à mettre quelque part en dur
    enum TypeCode
    {
    	FmtError = -1,
    	FmtInt32,
    	FmtInt64,
    	FmtUInt32,
    	FmtUInt64,
    	FmtDouble,
    	FmtLongDouble,
    	FmtString
    };
     
    TypeCode getPercentCode(char const *fmt, size_t &offset)
    {
    	char const *pPercent = strchr(fmt+offset, '%');
    	if(pPercent==NULL)
    		return TypeCode; //Erreur, trop d'arguments
    	char c;
    	int cptSmallL = 0;
    	int cptBigL = 0;
    	for(offset =(fmt-pPercent)+1 ; fmt[offset++] != '\0' ; )
    	{
    		switch(c)
    		{
    		case '\0': return FmtError;
    		case 'f': return (cptBigL >= 1 ? FmtLongDouble : FmtDouble);
    		case 'd': return (cptSmallL >= 2 || (cptSmallL >= 1 && sizeof(long)!=sizeof(int))) ? FmtInt64 : FmtInt32;
    		case 'x':
    		case 'u': return (cptSmallL >= 2 || (cptSmallL >= 1 && sizeof(long)!=sizeof(int))) ? FmtUInt64 : FmtUInt32;
    		case 's': return FmtString;
    		//etc.
    		//Note: Sur une plate-forme 64-bits, il faudrait aussi gérer les préfixes de longueur,
    		//et peut-être retourner une lettre majuscule pour les long / long long...
    		case '%':
    			//Cas spécial, il faut recommencer l'analyse
    			//car ce n'était pas un champ correspondant à un argument:
    			pPercent = strchr(fmt+offset, '%');
    			if(pPercent==NULL)
    				return FmtError; //Erreur, trop d'arguments
    			offset = (fmt-pPercent)+1;
    		}
    	}
    	offset = strlen(fmt); //Pour être sûr de ne pas pointer derrière le caractère nul terminal.
    	return FmtError;
    }
     
    template< class T >
    struct FormatForType { };
     
    template<> struct FormatForType<int> { static const TypeCode fmt=FmtInt32; };
    template<> struct FormatForType<unsigned int> { static const TypeCode fmt=FmtUint32; };
    template<> struct FormatForType<double> { static const TypeCode fmt=FmtDouble; }
    template<> struct FormatForType<std::string> { static const TypeCode fmt=FmtString; }
    template<> struct FormatForType<char const*> { static const TypeCode fmt=FmtString; }
    template<> struct FormatForType<char*> { static const TypeCode fmt=FmtString; }
    //etc.
     
    void throwFormatError(int argNum, TypeCode inFormat, TypeCode inType);
     
    template<typename Type1, typename Type2, typename Type3, typename Type4, typename Type5, typename Type6, typename Type7, typename Type8, typename Type9>
    void tracer9(const char * fmt, Type1 type1, Type2 type2, Type3 type3, Type4 type4, Type5 type5, Type6 type6, Type7 type7, Type8 type8, Type9 type9)
    {
    	char code;
    	size_t offset = 0;
    	if((code=getPercentCode(fmt, offset)) != FormatForType<Type1>::fmt) { throwFormatError(1, code, FormatForType<Type1>::fmt); }
    	if((code=getPercentCode(fmt, offset)) != FormatForType<Type2>::fmt) { throwFormatError(2, code, FormatForType<Type2>::fmt); }
    	if((code=getPercentCode(fmt, offset)) != FormatForType<Type3>::fmt) { throwFormatError(3, code, FormatForType<Type3>::fmt); }
    	if((code=getPercentCode(fmt, offset)) != FormatForType<Type4>::fmt) { throwFormatError(4, code, FormatForType<Type4>::fmt); }
    	if((code=getPercentCode(fmt, offset)) != FormatForType<Type5>::fmt) { throwFormatError(5, code, FormatForType<Type5>::fmt); }
    	if((code=getPercentCode(fmt, offset)) != FormatForType<Type6>::fmt) { throwFormatError(6, code, FormatForType<Type6>::fmt); }
    	if((code=getPercentCode(fmt, offset)) != FormatForType<Type7>::fmt) { throwFormatError(7, code, FormatForType<Type7>::fmt); }
    	if((code=getPercentCode(fmt, offset)) != FormatForType<Type8>::fmt) { throwFormatError(8, code, FormatForType<Type8>::fmt); }
    	if((code=getPercentCode(fmt, offset)) != FormatForType<Type9>::fmt) { throwFormatError(9, code, FormatForType<Type9>::fmt); }
     
    	if(strchr(fmt+offset, '%') != NULL)
    		throw std::invalid_argument("Il y a plus de pourcents dans le format que d'arguments passés à la fonction!");
     
    	tracer(fmt, type1, type2, type3, type4, type5, type6, type7, type8, type9);
    }
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

Discussions similaires

  1. Transmission d'arguments sur une fonction anonyme
    Par Sylvercis dans le forum jQuery
    Réponses: 5
    Dernier message: 11/02/2015, 02h22
  2. Réponses: 2
    Dernier message: 22/06/2011, 16h21
  3. Réponses: 6
    Dernier message: 06/05/2008, 18h52
  4. Récupération d'arguments sur une fonction
    Par p0Kep0K dans le forum Général JavaScript
    Réponses: 8
    Dernier message: 21/09/2005, 10h17
  5. PerlDoc sur une fonction d'un module
    Par lesouriciergris dans le forum Modules
    Réponses: 2
    Dernier message: 13/03/2003, 21h50

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