Partage
  • Partager sur Facebook
  • Partager sur Twitter

La LibC des Zéros

Objectif : recoder la libC.

1 août 2012 à 14:57:04

Citation : damjuve

Citation : Eyyub

@Julienf: Il fallait pas ouvrir ta bouche, tu te fais lyncher grave.


Bah en même temps je suis pas sure qu'il ait déjà ouvert les headers pour voir toute les fonctions qu'il y avait dedans. C'est sure que si LibC = LibC selon Epitech ça va pas te prendre trop longtemps à recoder, après si t'es fier de savoir recoder un strlen super vite ...


Ça c'est sûr !
  • Partager sur Facebook
  • Partager sur Twitter
1 août 2012 à 14:59:58

Bonjour,

L'idée est sympa. C'est comme ça que j'ai commencé à apprendre le C. :)

Ma petite contrib pour stdlib : qsort, bsearch, srand et rand (tirée droit de mes archives :p ).

/**
Tri tableau generique
**/
#define swap(p1,p2,sz)          {                               \
                                    size_t j;                   \
                                    for (j=0; j<sz; j++)        \
                                    {                           \
                                        char ptmp = *(p1+j);    \
                                        *(p1+j) = *(p2+j);      \
                                        *(p2+j) = ptmp;         \
                                    }                           \
                                }

void qsort (void *base, size_t nmemb, size_t size, int (*compar)(const void*, const void *))
{
    char *debut, *fin, *gauche, *droite, *pivot;

    debut = base;
    fin = debut+(size*nmemb)-size;

    while (debut < fin)
    {
        gauche = debut;
        droite = fin;

        /* on choisit un pivot au milieu du tableau,
        (et on le place au début pour le partitionnement) */
        swap (debut, debut+(((fin-debut)/(size*2))*size), size);
        pivot = debut;

        /* Partitionnement */
        do
        {
            while (gauche < droite && compar(droite, pivot) > 0)
                droite -= size;
            swap (gauche, droite, size);
            pivot = droite;

            while (gauche < droite && compar(gauche, pivot) <= 0)
                gauche += size;
            swap (droite, gauche, size);
            pivot = gauche;
        }
        while (gauche < droite);

        /* Pour minimiser les appels récursifs */
        if (gauche-debut < fin-gauche)
        {
            qsort(debut, (gauche-debut)/size, size, compar);
            debut = gauche+size;
        }
        else
        {
            qsort(gauche+size, (fin-gauche)/size, size, compar);
            fin = gauche-size;
        }
    }
}

void* bsearch (const void *key, const void *base, size_t nmemb, size_t size, int (*compar) (const void *, const void *))
{
    char* debut = (char*) base;
    char* fin = debut + (nmemb*size) - size;
    char* median;
    int cmp;
    while (debut < fin)
    {
        median = debut + ((nmemb/2)*size);
        cmp = compar(key, median);
        if (!cmp)
            return median;
        else if (cmp > 0)
            debut = median + size;
        else
            fin = median - size;
        nmemb = (fin-debut)/size;
    }
    return compar(key, debut) ? NULL : debut;
}



/**
Nombres aleatoires
**/
static int r_seed = 1;

void srand (unsigned int seed)
{
   r_seed = seed;
}

int rand (void)
{
   r_seed = ( ( (1103515245 * r_seed) + 12345 ) & 0x7fffffff );
   return (r_seed);
}


Et voici strtok pour compléter string.h
char *strtok (char *s, const char *delim)
{
    static char *mem = NULL;
    char *occ;

    if (s == NULL)
        s = mem;

    while ( (occ = strstr(s, delim)) == s )  // evite les tokens vides
    {
        s += strlen(delim);
        *occ = '\0';
    }

    if ( occ != NULL )
    {
        mem = occ + strlen(delim);
        *occ = '\0';
    }
    else
        mem = NULL;

    return s;
}


Sinon, les fonctions strtol et compagnie sont aussi très intéressantes à écrire, mais il y a des dépendances avec <errno.h>, et puis ce serait bien de laisser un peu tout le monde participer.

@ Julienf : Je te propose que tu nous écrive malloc et compagnie pour participer (from scratch, bien entendu). C'est un exercice intéressant quel que soit ton niveau ! ;)

Citation : mcgee42

C'est beau de voir le nombre de tech qu'il y a ici pensant pouvoir refaire toutes une lib C car ils en ont fait une partie en piscine de première année et quelques plus délicate en seconde année. Ceci dit, il est vrai que printf n'est pas vraiment difficile à réaliser (et ce dans son ensemble, n'en déplaise à rz0).


Ben vas y, où est le code ?
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
1 août 2012 à 15:09:36

J'aimerais bien coder fwrite et fread, faut que je réfléchisse à comment faire ça proprement.
  • Partager sur Facebook
  • Partager sur Twitter
1 août 2012 à 15:09:44

Citation

Ma petite contrib pour stdio : qsort, bsearch, srand et rand (tirée droit de mes archives :p ).



stdlib.h plutôt.

Citation

Sinon, les fonctions strtol et compagnie sont aussi très intéressantes à écrire, mais il y a des dépendances avec <error.h>



Tu parles de errno.h ? De toute manière, des dépendances il y en a beaucoup !

@inf0 : Pour ça, il faut déjà définir l'implémentation de l'objet FILE. ;)
  • Partager sur Facebook
  • Partager sur Twitter
Staff désormais retraité.
Anonyme
1 août 2012 à 15:12:14

Citation : Lucas-84

@inf0 : Pour ça, il faut déjà définir l'implémentation de l'objet FILE. ;)



C'est vrai, mais de ce côté, autant tricher et se servir des en-têtes fournis avec le compilateur. :)

Ps : ci-dessous une version minimale de printf, mais elle n'est pas totalement indépendante puisque elle réutilise certaines fonctions déjà codées et elle n'est pas complète.

#include <stdarg.h>
#include <stdio.h>

#define BUFF_SIZE   256

void my_putchar(char c)
{
    fwrite(&c, sizeof(char), 1, stdout);
}

int itos(int nombre, int base)
{
    char buff[BUFF_SIZE];
    char lettre[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    int cmp, temp, res;

    cmp = temp = res = 0;

    if (nombre < 0)
        res++, putchar('-');

    do
    {
        temp = nombre % base;
        temp = (temp < 0) ? -temp : temp;
        buff[cmp] = lettre[temp];
        cmp++;
        nombre /= base;

    } while (nombre != 0);

    for (cmp = cmp - 1; cmp >= 0; cmp--)
    {
        putchar(buff[cmp]);
        res++;
    }


    return res;
}

int my_vprintf(const char * str, va_list args)
{
    int i, res;

    i = res = 0;

    for (i = 0; str[i] != '\0';i++)
    {
        switch(str[i])
        {
            case '%':
                i++;
                switch(str[i])
                {

                    case 'b':
                    {
                        int entier = va_arg(args, int);
                        res += itos(entier, 2);
                        i++;
                    }
                    break;

                    case 'o':
                    {
                        int entier = va_arg(args, int);
                        my_putchar('0');
                        res += itos(entier, 8) + 1;
                        i++;
                    }
                    break;

                    case 'i':
                    case 'd':
                    {
                        int entier = va_arg(args, int);
                        res += itos(entier, 10);
                        i++;
                    }
                    break;

                    case 'u':
                    {
                        unsigned int entier = va_arg(args, unsigned int);
                        res += itos(entier, 10);
                        i++;
                    }
                    break;

                    case 'X':
                    {
                        int entier = va_arg(args, int);
                        my_putchar('0');
                        my_putchar('x');
                        res += itos(entier, 16) + 2;
                        i++;
                    }
                    break;

                    case 'c':
                    {
                        char c = (unsigned char)va_arg(args, int);
                        my_putchar(c);
                        i++, res++;
                    }
                    break;

                    case 's':
                    {
                        char * s = va_arg(args, char *);
                        for (; *s != '\0'; s++)
                        {
                            my_putchar(*s);
                            res++;
                        }

                        i++;
                    }
                    break;

                    case '%':
                        my_putchar('%');
                        res++;
                    break;

                    default:
                        my_putchar('%');
                        res++;
                    break;
                }

            default:
                my_putchar(str[i]);
                res++;
            break;
        }
    }

    return res;
}

int my_printf(const char * str, ...)
{
    int res;
    va_list ap;

    va_start(ap, str);

    res = my_vprintf(str, ap);

    va_end(ap);

    return res;
}

int main(void)
{
    int a, b;

    a = my_printf("%d %i %c %s\n", 24577, -21, 'a', "Sa");
    b = printf("%d %i %c %s\n", 24577, -21, 'a', "Sa");

    printf("%d %d \n", a, b);

    a = my_printf("Salut\n");
    b = printf("Salut\n");

    printf("%d %d \n", a, b);

    printf("%d %u\n", -42 , -42);
    my_printf("%d %u\n", -42 , -42);

    my_printf("%d %X %o\n", 47, 47, 47);
    my_printf("%d %X %b\n", 0xAA55, 0xAA55, 0xAA55);
    my_printf("%d %X %o %b\n", 11, 11, 11, 11);

    my_printf("%d\n", 3);

    return 0;
}
  • Partager sur Facebook
  • Partager sur Twitter
1 août 2012 à 15:19:39

Il y a quelques constantes et macro à définir (stdio.h):
EOF, BUFSIZ, FILENAME_MAX, FOPEN_MAX, _IOFBF, _IOLBF, _IONBF, L_tmpnam, NULL, SEEK_CUR, SEEK_END, SEEK_SET, TMP_MAX.

et quelques structure (stdio.h):
FILE (qui contient un file descriptor, la position du stream actuel, la position de fin de fichier, un integer d'erreur et un pointeur sur le buffer), fpos_t et size_t.

et quelques variables globales:
stdin, stdout, stderr ...

En se fiant aux normes pour définir tout ce petit bordel, le reste n'est pas trop compliqué, et c'est assez intéressant à coder je pense.
  • Partager sur Facebook
  • Partager sur Twitter
1 août 2012 à 15:23:27

@inf0 : C'est un bon début, mais il manque tous les spécificateurs de longueur, les flottants, les largeurs de champ, la précision, les flags, etc.
@mcgee42 : Les E/S c'est pas ce qu'il y a de plus simple à recoder. Rien que les constantes, en fonction de quoi tu les fixes ? Normalement, il faut se fier aux détails de l'ABI ; or, c'est difficile de faire ça de manière portable.
  • Partager sur Facebook
  • Partager sur Twitter
Staff désormais retraité.
1 août 2012 à 15:23:57

Citation : informaticienzero

for (i = 0; str[i] != '\0';i++)
    {
        switch(str[i])
        {
            case '%':
                i++;
                switch(str[i])
                {

                    case 'b':
                    {
                        int entier = va_arg(args, int);
                        res += itos(entier, 2);
                        i++;
                    }
                    break;

                    case 'o':
                    {
                        int entier = va_arg(args, int);
                        my_putchar('0');
                        res += itos(entier, 8) + 1;
                        i++;
                    }
                    break;

                    case 'i':
                    case 'd':
                    {
                        int entier = va_arg(args, int);
                        res += itos(entier, 10);
                        i++;
                    }
                    break;

                    case 'u':
                    {
                        unsigned int entier = va_arg(args, unsigned int);
                        res += itos(entier, 10);
                        i++;
                    }
                    break;

                    case 'X':
                    {
                        int entier = va_arg(args, int);
                        my_putchar('0');
                        my_putchar('x');
                        res += itos(entier, 16) + 2;
                        i++;
                    }
                    break;

                    case 'c':
                    {
                        char c = (unsigned char)va_arg(args, int);
                        my_putchar(c);
                        i++, res++;
                    }
                    break;

                    case 's':
                    {
                        char * s = va_arg(args, char *);
                        for (; *s != '\0'; s++)
                        {
                            my_putchar(*s);
                            res++;
                        }

                        i++;
                    }
                    break;

                    case '%':
                        my_putchar('%');
                        res++;
                    break;

                    default:
                        my_putchar('%');
                        res++;
                    break;
                }

            default:
                my_putchar(str[i]);
                res++;
            break;
        }
    }


Super Maintenable ça, t'aurais pu créer un tableau de pointeurs sur fonction/clef quant même.
  • Partager sur Facebook
  • Partager sur Twitter
Si debugger, c’est supprimer des bugs, alors programmer ne peut être que les ajouter - Edsger Dijkstra
Anonyme
1 août 2012 à 15:27:14

Citation : damjuve

Super Maintenable ça, t'aurais pu créer un tableau de pointeurs sur fonction/clef quant même.



J'y ai pas pensé sur le coup. Je veux bien un exemple vite fait pour voir la façon de faire, parce que là je vois pas trop comment procéder.
  • Partager sur Facebook
  • Partager sur Twitter
1 août 2012 à 15:31:06

Citation

Super Maintenable ça, t'aurais pu créer un tableau de pointeurs sur fonction/clef quant même.



Je suis d'accord que ce n'est pas super maintenable, mais le tableau de pointeurs sur fonction ne me semble pas être une bonne idée. AMHA la bonne solution est de diviser le code en fonctions plus précises et éviter de faire les calculs dans l'aiguillage directement. Sinon, avec les prototypes, ça risque d'être difficile à... maintenir justement. Bref, je ne suis personnellement pas favorable à la solution pointeur sur fonctions, mais à la modularisation.
  • Partager sur Facebook
  • Partager sur Twitter
Staff désormais retraité.
1 août 2012 à 15:32:17

@informaticienzero: tuto ^^ (à mon époque c'est l'un des rares cas de variable globale que les aer et astek acceptaient).

  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
1 août 2012 à 15:34:57

Citation : mcgee42

@informaticienzero: tuto ^^ (à mon époque c'est l'un des rares cas de variable globale que les aer et astek acceptaient).



Oui les pointeurs sur fonctions je sais ce que c'est, j'en ai déjà utilisé. Je veux dire que dans le cas présent, pour ce code, je ne sais pas comment faire.
  • Partager sur Facebook
  • Partager sur Twitter
1 août 2012 à 15:36:00

Citation : informaticienzero

Citation : damjuve

Super Maintenable ça, t'aurais pu créer un tableau de pointeurs sur fonction/clef quant même.



J'y ai pas pensé sur le coup. Je veux bien un exemple vite fait pour voir la façon de faire, parce que là je vois pas trop comment procéder.



J'Edit ton code ;)

Citation : Lucas-84

Citation

Super Maintenable ça, t'aurais pu créer un tableau de pointeurs sur fonction/clef quant même.



Je suis d'accord que ce n'est pas super maintenable, mais le tableau de pointeurs sur fonction ne me semble pas être une bonne idée. AMHA la bonne solution est de diviser le code en fonctions plus précises et éviter de faire les calculs dans l'aiguillage directement. Sinon, avec les prototypes, ça risque d'être difficile à... maintenir justement. Bref, je ne suis personnellement pas favorable à la solution pointeur sur fonctions, mais à la modularisation.


Ouai m'enfin si tu découpe en fonction, tu te retrouve avec des fonctions, donc des prototypes, donc je vois pas trop la différence si ce n'est que tu garde le switch et que c'est toujours aussi moche.

Citation : mcgee42

@informaticienzero: tuto ^^ (à mon époque c'est l'un des rares cas de variable globale que les aer et astek acceptaient).


Je confirme c'est toujours l'une des rares variables global utilisée.
  • Partager sur Facebook
  • Partager sur Twitter
Si debugger, c’est supprimer des bugs, alors programmer ne peut être que les ajouter - Edsger Dijkstra
1 août 2012 à 15:38:07

Je suis d'accord avec Lucas-84, utilisait des pointeurs de fonctions me parait plus compliqué dans ce cas là à maintenir qu'autres choses :/

Par contre, si je ne m'abuse, ton printf ne marche pas pour les unsigned int :
case 'd':
{
    int entier = va_arg(args, int);
    res += itos(entier, 10);
    i++;
}
break;
case 'u':
{
    unsigned int entier = va_arg(args, unsigned int);
    res += itos(entier, 10);
    i++;
}
break;

Tu utilises la même fonction pour les deux, auquel cas ton unsigned int va être converti à l'appel en... int :)
  • Partager sur Facebook
  • Partager sur Twitter
1 août 2012 à 15:40:42

Salut,

Citation : Julienf

Recoder la libC c'est de la branlette, avec de simple notion tu peux la recoder en partie assez facilement.



J'ai hâte de voir ton implémentation de setjmp et longjmp ;)

Sinon, histoire d'avoir une réponse productive, voici une implémentation de strtol

#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stddef.h>


long
strtol(char const * restrict s, char * restrict * p, int base)
{
	static long conv[] = {
		['0'] = 0,
		['1'] = 1,
		['2'] = 2,
		['3'] = 3,
		['4'] = 4,
		['5'] = 5,
		['6'] = 6,
		['7'] = 7,
		['8'] = 8,
		['9'] = 9,
		['a'] = 10,
		['b'] = 11,
		['c'] = 12,
		['d'] = 13,
		['e'] = 14,
		['f'] = 15,
		['A'] = 10,
		['B'] = 11,
		['C'] = 12,
		['D'] = 13,
		['E'] = 14,
		['F'] = 15,
	};
	long n = 0;
	int neg = (*s == '-') ? (++s, 1) : 0;

	if (base > 16)
		goto err;

	for (; *s != '\0'; ++s) {
		if (!isxdigit(*s))
			goto err;

		if (LONG_MAX / base < n) {
			n = (neg) ? LONG_MIN : LONG_MAX;
			errno = ERANGE;
			goto err;
		} else
			n *= base;

		if (LONG_MAX - conv[*s] < n) {
			n = (neg) ? LONG_MIN : LONG_MAX;
			errno = ERANGE;
			goto err;
		} else
			n += conv[*s];
	}

	goto ret;
err:
	if (p != NULL)
		*p = s;
ret:
	return (neg) ? -n : n;
}
  • Partager sur Facebook
  • Partager sur Twitter
1 août 2012 à 15:43:45

Utiliser des pointeurs de fonction ça n'a rien de complexe, peut être un chnouilla moins performant (non perceptible) mais pas plus complexe. et beaucoup plus simple à maintenir.

@Taurre: j'aime assez ton implémentation
  • Partager sur Facebook
  • Partager sur Twitter
1 août 2012 à 15:52:44

Citation : informaticienzero

Citation : damjuve

Super Maintenable ça, t'aurais pu créer un tableau de pointeurs sur fonction/clef quant même.



J'y ai pas pensé sur le coup. Je veux bien un exemple vite fait pour voir la façon de faire, parce que là je vois pas trop comment procéder.



Bon c'est vite fait, j'ai pas gérer res, j'ai pas mis les fonction flag_*, puisque basiquement elles correspondent au contenue de tes 'case'.

typedef struct     	s_flags
{
	char			c;
	void			(*func)(va_list args);
}			t_flags;

t_flags				flagsTab[] =
{
	{'d', &flag_d},
	{'i', &flag_d},
	{'c', &flag_c},
	{'s', &flag_s},
	{'u', &flag_u},
	{'X', &flag_X},
	{'o', &flag_o},
	{-1, NULL}
};

int my_vprintf(const char * str, va_list args)
{
    int i, res;

    i = res = 0;

    for (i = 0; str[i] != '\0';i++)
    {
        switch(str[i])
        {
            case '%':
                i++;
                for (int j = 0; falgsTab[j].c != -1; j++)
                {
		        if (flagsTab[j] == str[i])
		        {
			    flagsTab[i].func(args);
			    break;
		         }
	         }
	    break;

            default:
                my_putchar(str[i]);
                res++;
            break;
        }
    }

    return res;
}


Citation : Holt

Je suis d'accord avec Lucas-84, utilisait des pointeurs de fonctions me parait plus compliqué dans ce cas là à maintenir qu'autres choses :/



Dans ce cas explique moi en quoi :
Chercher le switch, chercher dans les cases si un flag a pas déjà été fait, l'ajouter et obtenir une fonction de 500 lignes à termes.
C'est plus maintenable que :
Ajouter une ligne au tableau, créer la fonction correspondante et son prototype.
  • Partager sur Facebook
  • Partager sur Twitter
Si debugger, c’est supprimer des bugs, alors programmer ne peut être que les ajouter - Edsger Dijkstra
1 août 2012 à 15:56:38

Citation : Lucas-84

Citation

Ma petite contrib pour stdio : qsort, bsearch, srand et rand (tirée droit de mes archives :p ).


stdlib.h plutôt.

Citation

Sinon, les fonctions strtol et compagnie sont aussi très intéressantes à écrire, mais il y a des dépendances avec <error.h>


Tu parles de errno.h ? De toute manière, des dépendances il y en a beaucoup !



Exact, corrigé.
  • Partager sur Facebook
  • Partager sur Twitter
1 août 2012 à 16:05:59

@damjuve : Bon, l'esperluette n'est pas obligatoire pour obtenir l'adresse de la fonction. Ensuite, on voit qu'il y a un parcours de boucle supplémentaire désagréable à la lecture et pour les performances (sans compter les déférencements nécessaires à chaque tour). De plus, toutes les fonctions n'auront pas le même prototype (p.ex. au niveau de la gestion des largeurs de champ), il faudra donc créer un pointeur de fonction sans spécifier le nombre d'argument, ce qui n'est pas une pratique très conseillée. Tu pourrais faire directement un tableau de pointeurs sur fonction pour avoir un accès direct (aux dépens de la complexité mémoire) et ainsi éviter ta boucle. Tu vas avoir des répétitions de code si tu traites tes arguments à chaque fois dans les fonctions utilitaires. Bref, là ça marche car le code est très simple, mais dès que tu feras des gestions plus profondes, ça va devenir le bordel. L'aiguillage est plus laxiste sur ce point.
  • Partager sur Facebook
  • Partager sur Twitter
Staff désormais retraité.
Anonyme
1 août 2012 à 16:18:45

Une version minimale et simpliste de stddef.h :

#ifndef STDDEF_H_INCLUDED
#define STDDEF_H_INCLUDED

#ifndef __STRICT_ANSI__
typedef signed long long int64_t;
typedef unsigned long long uint64_t;
#endif 

typedef signed char int16_t;
typedef unsigned char uint8_t;
typedef signed int int16_t;
typedef unsigned int uint16_t;
typedef signed long int32_t;
typedef unsigned long uint32_t;

typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef unsigned long QWORD;

typedef unsigned long size_t;
typedef long ssize_t;
typedef long ptrdiff_t;

typedef long time_t;
typedef long clock_t;

#define NULL  (void*)0

#endif


J'ai ignoré pas mal de trucs, et je ne sais pas comment faire pour séparer les ajouts du C99 des types déjà définis par le C ISO. J'ai essayé avec __STRICT_ANSI__ mais je suis pas sûr que ce soit ça.
  • Partager sur Facebook
  • Partager sur Twitter
1 août 2012 à 16:21:05

@inf0 : Ça sert à rien de refaire stddef.h, c'est dépendant de l'implémentation. Quelle certitude as-tu que ton signed long fasse 32 bits par exemple ?
  • Partager sur Facebook
  • Partager sur Twitter
Staff désormais retraité.
1 août 2012 à 16:21:31

@Lucas-84 :
Oui bah pour des cas plus complexe, j'utilise des façon de faire plus complexe, il voulait un exemple.

Pour la lecture tu dis que c'est moins agréable à lire, mais la boucle ne sera pas relue, et le switch n'est définitivement pas adapté lorsqu'il entraine ta fonction au delà des 100 lignes. Tu peux donner tout les arguments que tu veux et que j'accepterai, il n’empêche qu'un switch gigantesque dans une fonction, c'est illisible et in maintenable.
  • Partager sur Facebook
  • Partager sur Twitter
Si debugger, c’est supprimer des bugs, alors programmer ne peut être que les ajouter - Edsger Dijkstra
Anonyme
1 août 2012 à 16:24:20

Citation : Lucas-84

@inf0 : Ça sert à rien de refaire stddef.h, c'est dépendant de l'implémentation. Quelle certitude as-tu que ton signed long fasse 32 bits par exemple ?



Ouais c'est vrai, mais alors y'a la moitié de la LibC qu'on ne peut pas refaire alors.
  • Partager sur Facebook
  • Partager sur Twitter
1 août 2012 à 16:27:07

Citation : damjuve

@Lucas-84 :
Oui bah pour des cas plus complexe, j'utilise des façon de faire plus complexe, il voulait un exemple.

Pour la lecture tu dis que c'est moins agréable à lire, mais la boucle ne sera pas relue, et le switch n'est définitivement pas adapté lorsqu'il entraine ta fonction au delà des 100 lignes. Tu peux donner tout les arguments que tu veux et que j'accepterai, il n’empêche qu'un switch gigantesque dans une fonction, c'est illisible et in maintenable.


On a pas parlé d'un switch gigantesque. C'est sûr que faire la conversion des arguments directement dans la fonction vprintf posent des problèmes (cf. mon dernier post d'ailleurs, vu que personne n'a relevé :P), mais en modularisant ça devient plus lisible.

J'avais aussi utilisé les pointeurs quand j'avais essayé de recoder printf la première fois, je n'ai plus le code, mais c'était pas mal parti en sucette si mes souvenirs sont bon :p
  • Partager sur Facebook
  • Partager sur Twitter
1 août 2012 à 16:31:59

Citation : damjuve

@Lucas-84 :
Oui bah pour des cas plus complexe, j'utilise des façon de faire plus complexe, il voulait un exemple.

Pour la lecture tu dis que c'est moins agréable à lire, mais la boucle ne sera pas relue, et le switch n'est définitivement pas adapté lorsqu'il entraine ta fonction au delà des 100 lignes. Tu peux donner tout les arguments que tu veux et que j'accepterai, il n’empêche qu'un switch gigantesque dans une fonction, c'est illisible et in maintenable.



Un switch bien indenté, p.ex. dont chaque cas tient sur une ligne, est pourtant plus lisible qu'un tableau de pointeurs sur fonction. Et c'est pas bien de rester camper sur ses positions.

Tu as donc le choix entre (en reprenant ton exemple) :

switch (*++str) {
case 'd': case 'i': flag_d(args); break;
case 'c':           flag_c(args); break;
case 's': 	    flag_s(args); break;
case 'u': 	    flag_u(args); break;
case 'X': 	    flag_X(args); break;
case 'o': 	    flag_o(args); break;
}


Et :

typedef struct     	s_flags
{
	char			c;
	void			(*func)(va_list args);
}			t_flags;

t_flags				flagsTab[] =
{
	{'d', &flag_d},
	{'i', &flag_d},
	{'c', &flag_c},
	{'s', &flag_s},
	{'u', &flag_u},
	{'X', &flag_X},
	{'o', &flag_o},
	{-1, NULL}
};

for (int j = 0; falgsTab[j].c != -1; j++)
{
    if (flagsTab[j] == str[i])
    {
        flagsTab[i].func(args);
        break;
    }
}


Personnellement je penche vers la première solution, en sachant que ça sera plus flexible lors de l'agrandissement du code (modification des arguments p.ex.) et que c'est bien plus natif pour le langage machine. Et ce n'est pas de la répétition à ce niveau. Après, chacun ses goûts.

@inf0 : Oui, c'est ce que j'ai fait remarqué plusieurs fois dans ce sujet.
  • Partager sur Facebook
  • Partager sur Twitter
Staff désormais retraité.
1 août 2012 à 16:37:13

@Lucas-84 :

ah bah oui là présenté comme ça je prend :P, menfin tes vache dans ta comparaison, ça serait plutôt :

t_flags				flagsTab[] =
{
	{'d', &flag_d},
	{'i', &flag_d},
	{'c', &flag_c},
	{'s', &flag_s},
	{'u', &flag_u},
	{'X', &flag_X},
	{'o', &flag_o},
	{-1, NULL}
};

Vs
switch (*++str) {
case 'd': case 'i': flag_d(args); break;
case 'c':           flag_c(args); break;
case 's': 	    flag_s(args); break;
case 'u': 	    flag_u(args); break;
case 'X': 	    flag_X(args); break;
case 'o': 	    flag_o(args); break;
}


Niveau lisibilité c'est super proche, et je pense que c'est toi qui écrit le plus de caractères :p
Par ailleurs l'avantage de ma méthode c'est qu'elle fonctionnera pour n'importe quelle valeur à tester, le switch ne fonctionne que pour une valeur numérique (et si tu veux faire un strcmp hein).
Après dans le cas présent ta fonction est en effet légèrement plus rapide.
  • Partager sur Facebook
  • Partager sur Twitter
Si debugger, c’est supprimer des bugs, alors programmer ne peut être que les ajouter - Edsger Dijkstra
Anonyme
1 août 2012 à 16:40:52

@Holt : oui j'ai remarqué aussi que mon unsigned était buggué car j'utilise la même fonction que pour un int signed.

@Lucas : dans ce cas, on va faire une liste de ceux qui sont faisables sans trop de problèmes et ceux qui dépendent trop de l'implémentation :

  • <assert.h>
  • <ctype.h>
  • <errno.h>
  • <float.h>
  • <limits.h>
  • <locale.h>
  • <math.h>
  • <setjmp.h>
  • <signal.h>
  • <stdarg.h>
  • <stddef.h>
  • <stdio.h>
  • <stdlib.h>
  • <string.h>
  • <time.h>
  • Partager sur Facebook
  • Partager sur Twitter
1 août 2012 à 16:43:56

Citation

ah bah oui là présenté comme ça je prend :P, menfin tes vache dans ta comparaison, ça serait plutôt :



Ah non, un des désavantages ça reste quand même l'appel de la fonction (là tu utilises un for, c'est pénalisant !). :p Et ici pas besoin de faire de strcmp avec printf ; on ne gère que des modificateurs de longueur. L'avantage de ma méthode est également de permettre de gérer des différences d'argument entre les fonctions utilitaires (et il y en aura forcément, à part si tu gères ta liste directement dans la fonction appelée, mais c'est moche).

@informaticienzero : Pour setjmp et stdarg, ça me semble également difficile.
  • Partager sur Facebook
  • Partager sur Twitter
Staff désormais retraité.
1 août 2012 à 16:46:58

Citation : Lucas-84

Citation

ah bah oui là présenté comme ça je prend :P, menfin tes vache dans ta comparaison, ça serait plutôt :



Ah non, un des désavantages ça reste quand même l'appel de la fonction (là tu utilises un for, c'est pénalisant !). :p Et ici pas besoin de faire de strcmp avec printf ; on ne gère que des modificateurs de longueur. L'avantage de ma méthode est également de permettre de gérer des différences d'argument entre les fonctions utilitaires (et il y en aura forcément, à part si tu gères ta liste directement dans la fonction appelée, mais c'est moche).



Je faisais une comparaison en terme de visibilité, évidement qu'en terme de perf le switch sera meilleur que le for dès le moment ou le tableau comprend deux entrée.
C'est vrai qu'ici après coup il semble plus indiqué, mais bon au moins j'aurai présenté une alternative trop méconu.
  • Partager sur Facebook
  • Partager sur Twitter
Si debugger, c’est supprimer des bugs, alors programmer ne peut être que les ajouter - Edsger Dijkstra
Anonyme
1 août 2012 à 16:52:21

Citation : Lucas-84

@informaticienzero : Pour setjmp et stdarg, ça me semble également difficile. stdlib ça me semble être possible entièrement (à moins que j'oublie quelque chose).



Pour setjmp, c'est juste sauvegarder un état à un moment donné, il suffit d'utiliser une petite routine en assembleur qui sauvegarde les registres (setjmp) et une autre qui les restaure (longjmp). Pour stdlib.h, tu as des fonctions comme system, exit, abort ou les fonctions liées aux caractères étendus qui peuvent être gênantes.
  • Partager sur Facebook
  • Partager sur Twitter