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 :

écrire des chaînes de longueur indéfinie


Sujet :

C

  1. #1
    Membre confirmé
    Profil pro
    amateur
    Inscrit en
    Avril 2012
    Messages
    145
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : amateur
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2012
    Messages : 145
    Par défaut écrire des chaînes de longueur indéfinie
    Je cherche le moyen le plus simple et universel de coder en C la fonctionnalité habituelle dans d'autre langages qui n'impose pas de prédéfinir la longueur d'une chaîne à composer. Je veux dire qu'en C pour obtenir le texte d'une donnée (fonction du type tostring) ou composer un texte quelconque (genre avec sprintf) il faut passer une chaîne tampon de longueur fixe. Ce qui pour moi est à la fois ennuyeux et erronné car cela force à surdimensionner, et systématiquement, sans compter que parfois il n'y a pas du tout de longueur max prédéfinie (ça dépend d'un contenu variable: quelle est la longueur max de l'expression textuelle d'un tableau? et je parle même pas du cas général de sprintf).

    Mon objectif est d'envelopper (wrap) les fonctions de la lib dans des fonctions à moi, du type int_tostring(int i) (conversions) ou format(char * fmt, ...) (cas général de type sprintf). J'ai découvert le code %n qui permet de savoir combien d'octets ont été écris, ce qui est une grande aide. Mais c'est à postériori donc il faut malgré tout avoir d'abord passé une chaîne de longueur fixe. Comment font les auteurs de langages implantés en C?

    Touts pistes bienvenues,
    merci,
    Denis

  2. #2
    Membre Expert
    Avatar de kwariz
    Homme Profil pro
    Chef de projet en SSII
    Inscrit en
    Octobre 2011
    Messages
    898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Chef de projet en SSII
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2011
    Messages : 898
    Par défaut
    Salut,

    tu peux déjà consulter comment cette fonction est implémentée dans la gnulib, il s'agit de vasnprintf (v: paramètres passés en va_list, a: allocation automatique, s: dans une chaine, n:contrainte de longueur) : http://git.sv.gnu.org/gitweb/?p=gnul...b/vasnprintf.c - http://www.gnu.org/software/gnulib/M...ule=vasnprintf


    Tu vois que cela devient vite complexe pour faire quelque chose de portable et généraliste. Dans la gnulib sont disponibles des fonctions qui manquent à certains systèmes, des implémentations de fonctions standards mais plus sécurisées ou avec un comportement un peu différent.

  3. #3
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 498
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 498
    Billets dans le blog
    1
    Par défaut
    Je cherche le moyen le plus simple et universel de coder en C la fonctionnalité habituelle dans d'autre langages qui n'impose pas de prédéfinir la longueur d'une chaîne à composer.
    J'ai envie de dire : bienvenu en C

    C est un langage où la gestion de la mémoire est à ta charge. C'est assez désagréable pour faire des petits programmes où tu aimerais pouvoir ne pas te soucier de la longueur des chaines ; tu ne peux pas à moins de sortir la grosse cavalerie. Si tu ne veux pas te soucier de la gestion de la mémoire, le C n'est pas le langage qu'il te faut. C'est une des raisons pour lesquelles j'ai envie d'apprendre Python mais le temps me manque !


    il faut passer une chaîne tampon de longueur fixe. Ce qui pour moi est à la fois ennuyeux et erronné car cela force à surdimensionner, et systématiquement, sans compter que parfois il n'y a pas du tout de longueur max prédéfinie
    Si tu veux faire faire une couche d'abstraction, tu retrouveras le problème dans ta couche d'abstraction : il faudra y prévoir un peu large à chaque fois pour ne pas risquer un débordement.

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 398
    Par défaut
    En pur standard C90, la solution classique est:
    • strlen() de tous les arguments chaînes de caractères
    • plus un surdimensionnement constant pour chaque nombre
    • plus longueur des parties autres que spécificateurs dans ta chaîne de format
    • plus caractère nul terminal.


    En standard C99, tu appelles snprintf() avec une taille nulle et un buffer nul, elle retournera une longueur. Tu alloues cette longueur +1.

    Sinon tu as des fonctions spécifiques dans certains environnements: asprintf() sous certains unixoïdes (wrappe malloc()+sprintf()), _scprintf() sous Visual Studio (retourne la taille nécessaire), etc.
    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.

  5. #5
    Membre confirmé
    Profil pro
    amateur
    Inscrit en
    Avril 2012
    Messages
    145
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : amateur
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2012
    Messages : 145
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    En pur standard C90, la solution classique est:
    • strlen() de tous les arguments chaînes de caractères
    • plus un surdimensionnement constant pour chaque nombre
    • plus longueur des parties autres que spécificateurs dans ta chaîne de format
    • plus caractère nul terminal.
    Bien, je me voyais faire quelque chose comme ça. Ce serait un tout petit peu plus facile dans mon cas, car j'ai déjà un type 'Text' qui contient son "poids" (j'évite le terme longueur pour éviter la confusion entre longueur et index de chaîne et longueur et index de textes / caractères, dès qu'on sort de l'ascii). Du coup, je n'ai pas besoin de strlen().

    En standard C99, tu appelles snprintf() avec une taille nulle et un buffer nul, elle retournera une longueur. Tu alloues cette longueur +1.

    Sinon tu as des fonctions spécifiques dans certains environnements: asprintf() sous certains unixoïdes (wrappe malloc()+sprintf()), _scprintf() sous Visual Studio (retourne la taille nécessaire), etc.
    Aha! snprintf() a l'air d'être ce que je cherchais (je me disais bien qu'il y avait un outil quelque part pour éviter de réinventer la roue). Je vais étudier cela.

    Merci de vos infos,
    Denis

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 398
    Par défaut
    Le problème de snprintf(), c'est qu'en tant que fonction C99, elle n'existe pas telle quelle sous Visual Studio, pas même la version 2010: Microsoft s'assoit sur C99.

    Heureusement, il y a moyen de reproduire le comportement (bien qu'avec de moins bonnes performances, vu qu'on traite la chaîne deux fois) avec un tel code.
    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 confirmé
    Profil pro
    amateur
    Inscrit en
    Avril 2012
    Messages
    145
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : amateur
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2012
    Messages : 145
    Par défaut réponse prometteuse
    Citation Envoyé par Médinoc Voir le message
    [...]
    En standard C99, tu appelles snprintf() avec une taille nulle et un buffer nul, elle retournera une longueur. Tu alloues cette longueur +1.
    Alors, oui: avec vsnprintf, ça roule:
    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
     
    char * format (char * form, ...) {
       va_list args ; va_start (args, form) ;
       char *s, *s0 ;
       Uint weight ;
     
       // Determine weight (count of bytes) in output (NUL excluded):
       s0 = malloc (1 * sizeof(char)) ;
       assert (s0 != NULL) ;
       weight = vsnprintf (s0, 1, form, args) ;
       printf ("weight:%i\n", weight) ;
       assert (weight > 0) ;
       weight ++ ;                                  // for NUL terminator
       free (s0) ; s0 = NULL ;
     
       // Write into new string of that weight:
       s = malloc (weight * sizeof(char)) ;
       assert (s != NULL) ;
       weight = vsprintf (s, form, args) ;
    //~    printf ("weight:%i\n", weight) ;
     
       va_end (args) ;
       return s ;
    }
     
    void test () {
       char * s = format ("int:%05i float:%+9.3f string:'%9s'",
          123, 1.23, "123") ;
       puts (s) ;
    }
    Petits points à éclaircir:

    * Je n'ai pas trouvé moyen de repasser la liste d'arguments variadique à snprintf, alors j'ai dû utiliser vsnprintf avec les outils de stdarg.h.

    * Impossible de passer NULL comme string cible: --> segfault direct. Même chose si je passe une string de taille nulle.

    * Si j'utilise (v)sprintf (sans le 'n') et que je passe pas une première string de taille suffisante, j'obtiens une erreur de glibc:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    "Glibc detected *** free(): invalid next size (fast) ..."
    pour laquelle il y a largement de quoi lire (en anglais) sur le web, mais pas de solution évidente. C'est évidemment dû à un buffer overflow, mais l'erreur bizarrement pointe sur le free(S0) (chez les autres aussi).

    J'ai asprintf et cie à disposition sur mon système, mais je préférerais rester aussi standard que possible: raison pour laquelle j'essayais de me passer du 'v' et du 'n' de vnsprintf. Dans l'idéal, mon code devrait pouvoir compiler avec n'importe quel compilateur conforme, ou au moins n'importe quel variante de gcc, sur n'importe quelle plateforme, même ancienne.

    Denis

  8. #8
    Membre Expert
    Avatar de kwariz
    Homme Profil pro
    Chef de projet en SSII
    Inscrit en
    Octobre 2011
    Messages
    898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Chef de projet en SSII
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2011
    Messages : 898
    Par défaut
    Citation Envoyé par denispir Voir le message
    J'ai asprintf et cie à disposition sur mon système, mais je préférerais rester aussi standard que possible: raison pour laquelle j'essayais de me passer du 'v' et du 'n' de vnsprintf. Dans l'idéal, mon code devrait pouvoir compiler avec n'importe quel compilateur conforme, ou au moins n'importe quel variante de gcc, sur n'importe quelle plateforme, même ancienne.

    Denis
    Salut,

    une ou deux remarques en passant :

    *** assert n'est pas fait pour tester les erreurs runtime ou user !
    assert existe pour aider le développeur à trouver les erreurs de conception/programmation uniquement ***
    Suivant les options de compilations aucun code ne sera généré pour assert.



    Obtenir un code portable est complexe mais pas compliqué. D'autant plus que la fonction que tu essayes de mettre au point existe déjà et est mise à disposition dans la gnulib (pas la glibc ... regarde un lien que j'ai précédemment posté). Avec ça tu es sûr que :

    1. ça fonctionne
    2. c'est maintenu, testé, supporté
    3. c'est multiplateforme et multicompilateur.

    Si tu te restreins à gcc ... c'est encore plus de bonheur pour toi la gnulib

  9. #9
    Membre confirmé
    Profil pro
    amateur
    Inscrit en
    Avril 2012
    Messages
    145
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : amateur
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2012
    Messages : 145
    Par défaut
    Citation Envoyé par kwariz Voir le message
    Salut,

    une ou deux remarques en passant :

    *** assert n'est pas fait pour tester les erreurs runtime ou user !
    assert existe pour aider le développeur à trouver les erreurs de conception/programmation uniquement ***
    Suivant les options de compilations aucun code ne sera généré pour assert.
    D'accord. En fait, j'utilise assert pour le moment pour marquer les points où je dervrais faire une détection voire une gestion d'erreurs. Mais je n'ai pas encore de méthode standard (et de toute façon pas étudié la ou probablement les façon(s) dont on s'y prend en C). Tout ce que je sais, c'est que j'aimerais trouver une autre technique que renvoyer des codes d'erreur dans des (faux) paramètres ou (fausses) valeurs de retour. On verra

    Obtenir un code portable est complexe mais pas compliqué. D'autant plus que la fonction que tu essayes de mettre au point existe déjà et est mise à disposition dans la gnulib (pas la glibc ... regarde un lien que j'ai précédemment posté). Avec ça tu es sûr que :

    1. ça fonctionne
    2. c'est maintenu, testé, supporté
    3. c'est multiplateforme et multicompilateur.

    Si tu te restreins à gcc ... c'est encore plus de bonheur pour toi la gnulib
    D'accord à nouveau. Je m'y perds complètement entre les libc, glibc, gnulib (je savais même pas que ces deux-là sont des choses différentes, je croyais que c'étaient 2 noms pour la même collection de libs), sans parler de toutes les variantes comme pour les systèmes embarqués...
    Bon, j'ai du pain sur la planche!

    PS: J'avais bien suivi ton lien, au fait, et tenté d'étudier la fonction en question. Mais c'est beaucoup trop complexe (pour moi) du fait de la multitude de variantes (d'options, plate-formes, niveaux...) imbriquées. Le coeur de la fonctionnalité serait sans doute compréhensible, mais là...

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 398
    Par défaut
    Le Buffer Overflow est détecté lors du free() parce que c'est à ce moment-là que sont effectués des contrôles sur le bloc (notamment vérifier si des valeurs "magiques" situées juste après ont été écrasées).

    * Impossible de passer NULL comme string cible: --> segfault direct. Même chose si je passe une string de taille nulle.
    Tu veux dire que vsnprintf (NULL, 0, form, args) ne passe pas? Dans ce cas, tu peux signaler un bug, car c'est contraire à la norme:
    Citation Envoyé par n1256.pdf chapitre 7.19.6.5, verset 2, ou n1570.pdf chapitre 7.21.6.5, verset 2
    If n is zero, nothing is written,
    and s may be a null pointer.
    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 Expert
    Avatar de kwariz
    Homme Profil pro
    Chef de projet en SSII
    Inscrit en
    Octobre 2011
    Messages
    898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Chef de projet en SSII
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2011
    Messages : 898
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Tu veux dire que vsnprintf (NULL, 0, form, args) ne passe pas? Dans ce cas, tu peux signaler un bug, car c'est contraire à la norme:
    Salut,

    bah ... ce n'est pas un bug ^^ ; c'est juste une implémentation qui ne respecte pas la norme (qui devient une feature, qui est utilisée qui est rajoutée à la norme ...) : d'où la complexité pour créer du code portable. Il y a de nombreuses "infractions" :

    Citation Envoyé par Gnulib manual (git version 4/12/12)

    8.1120 vsnprintf

    POSIX specification: http://www.opengroup.org/onlinepubs/...vsnprintf.html

    Gnulib module: vsnprintf or vsnprintf-posix

    Portability problems fixed by either Gnulib module vsnprintf or vsnprintf-posix:

    • This function is missing on some platforms: IRIX 5.3, OSF/1 4.0, Solaris 2.5.1.

    • This function does not support format directives that access arguments in an arbitrary
    order, such as "%2$s", on some platforms: NetBSD 3.0, mingw, MSVC 9, BeOS.

    • This function overwrites memory even when a size argument of 1 is passed on some
    platforms: Linux libc5, BeOS.

    • This function does not return a byte count as specified in C99 on some platforms:
    HP-UX 11, IRIX 6.5, OSF/1 5.1, Solaris 9, mingw, MSVC 9.


    Portability problems fixed by Gnulib module vsnprintf-posix:
    • This function does not support size specifiers as in C99 (hh, ll, j, t, z) on some
    platforms: AIX 5.1, HP-UX 11.23, IRIX 6.5, OSF/1 5.1, Solaris 9, Cygwin 1.5.24,
    mingw, MSVC 9, BeOS.

    • printf of ‘long double’ numbers is unsupported on some platforms: mingw, MSVC 9,
    BeOS.

    • printf "%f", "%e", "%g" of Infinity and NaN yields an incorrect result on some platforms:
    AIX 5.2, OSF/1 5.1, Solaris 11 2011-11, mingw, MSVC 9.

    • This function does not support the ‘a’ and ‘A’ directives on some platforms: glibc-2.3.6,
    MacOS X 10.5, NetBSD 5.0, OpenBSD 4.0, AIX 5.2, HP-UX 11, IRIX 6.5, OSF/1 5.1,
    Solaris 11 2011-11, Cygwin 1.5.x, mingw, MSVC 9, BeOS.

    • This function does not support the ‘F’ directive on some platforms: NetBSD 3.0, AIX
    5.1, HP-UX 11.23, IRIX 6.5, OSF/1 5.1, Solaris 9, Cygwin 1.5.x, mingw, MSVC 9,
    BeOS.

    • This function does not support the ‘ls’ directive on some platforms: OpenBSD 4.0,
    IRIX 6.5, Solaris 2.6, Cygwin 1.5.x, Haiku.

    • This function does not support precisions in the ‘ls’ directive correctly on some plat-
    forms: Solaris 11 2011-11.

    • This function doesn’t support the ’ flag on some platforms: NetBSD 3.0, Cygwin
    1.5.24, mingw, MSVC 9.

    • This function behaves incorrectly when a ‘-’ flag and a negative width are specified
    together, on some platforms: HP-UX 10.20.

    • printf "%010f" of NaN and Infinity yields an incorrect result (padded with zeroes) on
    some platforms: MacOS X 10.5, FreeBSD 6.0, NetBSD 5.0, AIX 5.2, IRIX 6.5, OSF/1
    5.1, Solaris 11 2011-11, Cygwin 1.5.x, mingw, MSVC 9.

    • This function does not support precisions larger than 512 or 1024 in integer, floating-
    point and pointer output on some platforms: AIX 7.1, Solaris 10/x86, mingw, MSVC
    9, BeOS.

    • This function mishandles large floating point precisions (for example, formatting 1.0
    with ‘"%.511f"’) on some platforms: Solaris 10.

    • This function can crash in out-of-memory conditions on some platforms: MacOS X
    10.3, FreeBSD 6.0, NetBSD 5.0.

    • This function does not truncate the result as specified in C99 on some platforms:
    mingw, MSVC 9.

    • This function does not fully support the ‘n’ directive on some platforms: HP-UX 11,
    mingw, MSVC 9.

    • This function overwrites memory even when a zero size argument is passed on some
    platforms: HP-UX 11, OSF/1 5.1.


    Portability problems not fixed by Gnulib:
    • When formatting an integer with grouping flag, this function inserts thousands sepa-
    rators even in the "C" locale on some platforms: NetBSD 5

  12. #12
    Membre Expert
    Avatar de kwariz
    Homme Profil pro
    Chef de projet en SSII
    Inscrit en
    Octobre 2011
    Messages
    898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Chef de projet en SSII
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2011
    Messages : 898
    Par défaut
    Citation Envoyé par denispir Voir le message
    D'accord à nouveau. Je m'y perds complètement entre les libc, glibc, gnulib (je savais même pas que ces deux-là sont des choses différentes, je croyais que c'étaient 2 noms pour la même collection de libs), sans parler de toutes les variantes comme pour les systèmes embarqués...
    Bon, j'ai du pain sur la planche!
    libc = bibliothèque standard contenant au minimum ce qui est indiqué par la norme
    flou hein ? Normal, il y a eu plusieurs normes ... mais un noyau dur de fonctions existe.
    glibc = gnulibc = libc6 (parfois) = l'implémentation version FSF, il en existe d'autres MSVCRT est la version microsoft, uLibc une version dédiée à l'embarqué ...
    ensuite une libc particulière peut-être posix.2 compliant, microsoft compliant, ...
    gnulib = comme cela pose des problèmes de portabilité (la même fonction existe sur une autre plateforme mais a un comportement différent ou la fonction n'existe pas ou fait carrément autre chose), gnulib est une bibliothèque de sources, qui redéfinit les fonctions et les rend "plus" compatibles.

    PS: J'avais bien suivi ton lien, au fait, et tenté d'étudier la fonction en question. Mais c'est beaucoup trop complexe (pour moi) du fait de la multitude de variantes (d'options, plate-formes, niveaux...) imbriquées. Le coeur de la fonctionnalité serait sans doute compréhensible, mais là...
    Oui, le but n'était pas que tu suives exactement cet exemple, le but était plus de te montrer l'ampleur de la tâche.
    Néanmoins, si je comprends l'idée que tu as derrière la tête, ton but semble être de créer une bibliothèque de conteneurs (qui fait défaut à la libc) évolués. Si tu te restreins à pouvoir la compiler avec gcc sur de vieilles machine (=vielles distro linux), tu vas quand même devoir utiliser des #define pour générer du code différent suivant la version de la libc (libc5/libc6), voire de la version du compilateur (gcc <3, série 3, 4, ...).


    Disons que pour un début, il vaut mieux écrire du code lisible, correct qui fonctionne dans un environnement pas trop exotique ; si tu veux le portabiliser (=rendre portable ?) tu pourras toujours le faire par la suite.

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 398
    Par défaut
    Vue la vocation annoncée de Gnulib, si, planter sur vsnprintf(NULL), c'est un bug, crois-moi.
    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
    Membre Expert
    Avatar de kwariz
    Homme Profil pro
    Chef de projet en SSII
    Inscrit en
    Octobre 2011
    Messages
    898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Chef de projet en SSII
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2011
    Messages : 898
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Vue la vocation annoncée de Gnulib, si, planter sur vsnprintf(NULL), c'est un bug, crois-moi.

    Oui, dans ce cas on peut parler de bug ; la liste est juste la liste des bugs recensés par plateforme que l'utilisation de la version Gnulib permet de circonvenir ; il est plus simple (pour être multiplateforme) d'utiliser les sources de la Gnulib que de tester chaque cas suivant la plateforme, enfin tant qu'on veut maintenir un comportement constant. Il est aussi plus simple de rajouter la ligne "testé sur la plateforme <X>, sans garantie sur un autre plateforme".

  15. #15
    Membre confirmé
    Profil pro
    amateur
    Inscrit en
    Avril 2012
    Messages
    145
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : amateur
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2012
    Messages : 145
    Par défaut
    Citation Envoyé par kwariz Voir le message
    libc = bibliothèque standard contenant au minimum ce qui est indiqué par la norme
    flou hein ? Normal, il y a eu plusieurs normes ... mais un noyau dur de fonctions existe.
    glibc = gnulibc = libc6 (parfois) = l'implémentation version FSF, il en existe d'autres MSVCRT est la version microsoft, uLibc une version dédiée à l'embarqué ...
    ensuite une libc particulière peut-être posix.2 compliant, microsoft compliant, ...
    gnulib = comme cela pose des problèmes de portabilité (la même fonction existe sur une autre plateforme mais a un comportement différent ou la fonction n'existe pas ou fait carrément autre chose), gnulib est une bibliothèque de sources, qui redéfinit les fonctions et les rend "plus" compatibles.
    Oui, c'est ce que je commence à comprendre.
    Ca me semble un superbe but. Aussi, le fait d'être distribué (et prévu pour l'être) essentiellement en source pourrait être, dans l'abstrait, un énorme avantage. Surtout pour du C. Malheureusement, la "surcomplexité" introduite par la quantité de variantes et leurs intéractions rend ce code inutilisable (pour moi) comme source d'exemple, d'inspiration, voire même de ré-utilisation texto. Si je comprends pas ce qui se passe et comment et pourquoi j'ai beaucoup de mal même à simplement utiliser "bêtement" un truc...


    Oui, le but n'était pas que tu suives exactement cet exemple, le but était plus de te montrer l'ampleur de la tâche.
    Néanmoins, si je comprends l'idée que tu as derrière la tête, ton but semble être de créer une bibliothèque de conteneurs (qui fait défaut à la libc) évolués. Si tu te restreins à pouvoir la compiler avec gcc sur de vieilles machine (=vielles distro linux), tu vas quand même devoir utiliser des #define pour générer du code différent suivant la version de la libc (libc5/libc6), voire de la version du compilateur (gcc <3, série 3, 4, ...).


    Disons que pour un début, il vaut mieux écrire du code lisible, correct qui fonctionne dans un environnement pas trop exotique ; si tu veux le portabiliser (=rendre portable ?) tu pourras toujours le faire par la suite.
    Je vais resteindre totalement mes ambitions en ce qui concerne la portabilité: déjà, que ça tourne chez moi. Je me doutais pas de l'ampleur de ces problèmes-là en C. Finalement, les mondes programmatiques où il y a une et une seule véritable référence (dont quasiment tous les langages dynamiques courants), c'est pas si mal, même si l'absence d'alternative est parfois pesante et provoque sans doute un manque d'émulation.

    Sinon, oui dans un premier temps je crée quelques structures de plus haut niveau, disons le niveau minimal permettant un certain confort (par réinventer la roue toutes les 3 lignes de code) et une pleine sécurité tant que l'efficience n'impose pas des routines "unsafe".

    J'en profite pour dire quelques mots de mon projet à long terme: il s'agit d'implanter un langage que j'ai en tête; très chouette ;-) nommé "syn" pour l'instant. Dans mon esprit c'est le langage idéal pour modéliser des systèmes dynamiques, c'est-à-dire sans doute la plus grande partie de la programmation réelle et intéressante, par opposition à la fois aux problèmes "académiques" (anglicisme) et au développement parfois routinier de business.
    C'est dynamique mais compilé, avec des structures de base simples et universelles, un modèle d'exécution un peu original, un modèle de programmation un peu spécial à la fois très libre et très contraignant, plusieurs distinctions sémantiques rares, homoiconique et totalement réflexif (par conception). Voilà.

    Du côté implantation, je me suis rendu compte que ça revient essentiellement à implanter correctement, clairement et efficacement les structures de données. Celles-ci donnent les éléments et types de base qui vivent à runtime.Du fait de l'homoiconicité, ça donne également toutes les structures d'arrière-plan et le code qui vivent également à runtime.
    Et comme ces structures sont bien choisies ;-) (et peut-être inconsciemment du fait d'un certain bagage en parsing), elles se trouvent être l'idéal pour définir (1) des types de patterns (le mot français?) (2) des patterns de ces types-là, et surtout (3) les noeuds d'un arbre de parsing c'est-à-dire résultats de matching générés par ces patterns et (4) la représentation des parties du code qui sont justement la notation de ces structures de données. (Je veux dire: comme en Lisp la représentation d'un bout de code qui se trouve être la notation d'une liste est une liste qui contient la représentation des définitions de ses éléments. Un truc comme ça; pour toute structure de donnée.)
    Une fois que j'ai ça, l'homoiconicité fait une partie du reste: on pourrait par exemple dès l'instant exécuter (un interpréteur gratuit, même si j'en veux pas). Le gros boulot restant c'est trouver la traduction adéquate en C, et ça c'est pas gagné car C est loin d'être l'idéal comme cible de compilation. Mais les autres langages possible non plus. D'avance, je vois des problemes avec mes structures contrôle de flux qui ont une logique un peu différente (goto risque de servir ;-), sans parler des fonctions, actions, et opérations. J'ai pensé à la LLVM, et même étudié soigneusement son langage, c'est bien mieux comme cible mais encore trop ésotérique comme langage proprement dit, pour moi; et super lourdingue.

    Voilà,

    denis

  16. #16
    Membre confirmé
    Profil pro
    amateur
    Inscrit en
    Avril 2012
    Messages
    145
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : amateur
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2012
    Messages : 145
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Vue la vocation annoncée de Gnulib, si, planter sur vsnprintf(NULL), c'est un bug, crois-moi.
    Il va falloir que je vérifie ça. Je suis sûr du plantage avec NULL comme chaîne cible, mais à postériori je sais plus si le NULL était bien accompagné d'un nombre d'octets à écrire = 0.
    (J'ai pas les outils sous la main pour vérifier, là.)

    denis

  17. #17
    Membre confirmé
    Profil pro
    amateur
    Inscrit en
    Avril 2012
    Messages
    145
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : amateur
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2012
    Messages : 145
    Par défaut
    Citation Envoyé par kwariz Voir le message
    Oui, dans ce cas on peut parler de bug ; la liste est juste la liste des bugs recensés par plateforme que l'utilisation de la version Gnulib permet de circonvenir ; il est plus simple (pour être multiplateforme) d'utiliser les sources de la Gnulib que de tester chaque cas suivant la plateforme, enfin tant qu'on veut maintenir un comportement constant. Il est aussi plus simple de rajouter la ligne "testé sur la plateforme <X>, sans garantie sur un autre plateforme".
    Mais quelle est la garantie de portabilité, dans le monde C, d'un prog avec un compilateur donné comme gcc à travers /ses/ variantes développées pour la variété de plate-formes visées? Est-ce qu'on peut se dire que ça va rouler (tant qu'on n'utilise pas directement des fionctionnalités propres à une sytème ou un hardware, genre via des appels système, ou tant qu'on le fait en implantant la même fonctionnalité pour les divers systèmes visés)? Est-ce qu'en fait un compilateur multi-plate-forme comme gcc n'établit pas une sorte de norme // de fait? Ou est-ce qu'au contraire il s'adapte juste au mieux à chacun de ces système en ayant des différences significatives de comportement, de conformité et de "largeur de couverture" de la norme?

    denis

  18. #18
    Membre Expert
    Avatar de kwariz
    Homme Profil pro
    Chef de projet en SSII
    Inscrit en
    Octobre 2011
    Messages
    898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Chef de projet en SSII
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2011
    Messages : 898
    Par défaut
    gcc et glibc font très bon ménage sur une foultitude de plateformes et sont très "généralistes". Si tu te restreins au couple (gcc,glibc) tu couvres de nombreuses plateformes. Ensuite si les fonctions mises à disposition sont documentées (oui oui ...), et tu sais quand elles sont apparues, à quelle norme se référer, etc ...
    Par exemple dans le cas de chaînes dynamiquement extensibles, tu peux utiliser open_memstream, c'est portable dans le sens "c'est du C sans utiliser des extensions du compilateur" et "ce n'est pas une fonction ajoutée à la glibc en dehors de normes officielles". Mais ça ne fonctionnera que sur les plateformes qui ont une libc conforme à la norme POSIX1-2008 (donc relativement récente et peu implentée).

  19. #19
    Membre confirmé
    Profil pro
    amateur
    Inscrit en
    Avril 2012
    Messages
    145
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : amateur
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2012
    Messages : 145
    Par défaut merci
    Bon, je crois qu'il y a assez de matière à explorer, là. Ma conclusion actualle est qu'il n'y a pas de méthode standard ou idiomatique en C. Il s'agit essentiellement de bien choisir ses outils libs et pour ça il y a beaucoup de boulot d'exploration, expérimentation, évaluation...

    Je pars néanmoins à la base sur le dernier conseil de kwariz: gcc et glibc. Ca me paraît de bon sens vu que gcc est de loin le compilo le plus utilisé (et sûrement le plus multi-plateforme).

    Je clos ce fil. Merci encore à vous.
    denis

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

Discussions similaires

  1. [Débutant] Déclarer des chaînes de longueur fixe
    Par Claude_Azoulai dans le forum C#
    Réponses: 9
    Dernier message: 29/03/2013, 19h49
  2. Réponses: 4
    Dernier message: 06/09/2005, 22h41
  3. [D6] Comment remplacer des chaînes dans un fichier ?
    Par fabien25 dans le forum Langage
    Réponses: 4
    Dernier message: 15/06/2005, 14h37
  4. Réponses: 9
    Dernier message: 05/04/2005, 10h39
  5. Passage des chaînes de caractères à une application CGI
    Par Thom@s dans le forum Composants VCL
    Réponses: 10
    Dernier message: 03/12/2004, 01h13

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