1530

si, mais ça ne résout pas le problème présent.

1531

Si, avec des codes d'erreurs et en utilisant le fallthrough, ça peut être utilisé, mais c'est tordu par rapport à des goto directs.
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

1532

Je vois déjà un intérêt au C++, la concision (ok, là je pourrais faire mieux). Mais je me trouve avec un de ces monstres pour un seul module, et encore il est simple... eek
monstre
/** loadCareers
  *
  * Initialize the Careers module. First read the save file, then create the interface.
  *
  * Return a pointer to itself, or NULL if something failed.
  **/

MODULE_DATA* loadCareers()
{
    MODULE_DATA* data;
    uint16_t numberEntries = 0;
    FILE* scoresFile;

    data = malloc (sizeof (MODULE_DATA));
    if (data != NULL)
    {
    /** Open file and read it **/

        scoresFile = fopen ("careers", "rb");
        if (scoresFile != NULL)
        {
            fread (&numberEntries, sizeof (uint16_t), 1, scoresFile); /* Number of entries */
            if (feof (scoresFile) == 0)
            {
                if ((numberEntries > 0) && (numberEntries <= 5))
                {
                    data->customData = malloc (sizeof (CAREER_DATA));
                    if (data->customData != NULL)
                    {
                        ((CAREER_DATA*) data->customData)->numEntries = numberEntries;
                        ((CAREER_DATA*) data->customData)->careersPtr = malloc (sizeof (ENTRY_CAREER) * numberEntries);
                        if (((CAREER_DATA*) data->customData)->careersPtr != NULL)
                        {
                            fread (((CAREER_DATA*) data->customData)->careersPtr, sizeof (ENTRY_CAREER), numberEntries, scoresFile);
                            if (ferror (scoresFile) != 0)
                            {
                                fclose (scoresFile);
                                free (((CAREER_DATA*) data->customData)->careersPtr);
                                free (data->customData);
                                data->customData = NULL;
                                numberEntries = 0;
                                fprintf (stderr, fileCareersInvalid);
                            }
                            fclose (scoresFile);
                        }
                        else    /* memory error */
                        {
                            fclose (scoresFile);
                            free (data->customData);
                            free (data);
                            error = EXIT_FAILURE;
                            fprintf (stderr, outOfMemory);
                            return NULL;
                        }
                    }
                    else    /* memory error */
                    {
                        fclose (scoresFile);
                        free (data);
                        error = EXIT_FAILURE;
                        fprintf (stderr, outOfMemory);
                        return NULL;
                    }

                }
                else    /* invalid number of careers */
                {
                    data->customData = NULL;
                    fprintf (stderr, fileCareersInvalid);
                    fclose (scoresFile);
                    numberEntries = 0;
                }
            }
            else    /* eof encountered at opening, file doesn't exist */
            {
                data->customData = NULL;
                fclose (scoresFile);
                numberEntries = 0;
            }
        }
        else    /* error when opening 'careers' */
        {
            data->customData = NULL;
            fprintf (stderr, couldntOpenCareers);
            numberEntries = 0;
        }


    /** Create interface **/

        data->numIcons = 6;
        data->iconList = malloc (data->numIcons * sizeof(ICON));
        if (data->iconList == NULL)
        {
            if (data->customData != NULL)
            {
                free (((CAREER_DATA*) data->customData)->careersPtr);
                free (data->customData);
            }
            free (data);
            fprintf (stderr, outOfMemory);
            error = EXIT_FAILURE;
            return NULL;
        }
        if (newIcon (&data->iconList[0], 521, 413, ICON_ENABLED, comeBack, "spr/icon_valid.png", NULL) == 0)
        {
            if (data->customData != NULL)
            {
                free (((CAREER_DATA*) data->customData)->careersPtr);
                free (data->customData);
            }
            free (data);
            return NULL;
        }
        if (newIcon (&data->iconList[1], 78, 237, (numberEntries > 0 ? ICON_ENABLED : ICON_DISABLED),
                     career1, "spr/icon_book.png", "spr/icon_book_d.png") == 0)
        {
            deleteNIcons (data->iconList, 1);
            if (data->customData != NULL)
            {
                free (((CAREER_DATA*) data->customData)->careersPtr);
                free (data->customData);
            }
            free (data);
            return NULL;
        }
        if (newIcon (&data->iconList[2], 78, 271, (numberEntries > 1 ? ICON_ENABLED : ICON_DISABLED),
                     career2, "spr/icon_book.png", "spr/icon_book_d.png") == 0)
        {
            deleteNIcons (data->iconList, 2);
            if (data->customData != NULL)
            {
                free (((CAREER_DATA*) data->customData)->careersPtr);
                free (data->customData);
            }
            free (data);
            return NULL;
        }
        if (newIcon (&data->iconList[3], 78, 305, (numberEntries > 2 ? ICON_ENABLED : ICON_DISABLED),
                     career3, "spr/icon_book.png", "spr/icon_book_d.png") == 0)
        {
            deleteNIcons (data->iconList, 3);
            if (data->customData != NULL)
            {
                free (((CAREER_DATA*) data->customData)->careersPtr);
                free (data->customData);
            }
            free (data);
            return NULL;
        }
        if (newIcon (&data->iconList[4], 78, 339, (numberEntries > 3 ? ICON_ENABLED : ICON_DISABLED),
                     career4, "spr/icon_book.png", "spr/icon_book_d.png") == 0)
        {
            deleteNIcons (data->iconList, 4);
            if (data->customData != NULL)
            {
                free (((CAREER_DATA*) data->customData)->careersPtr);
                free (data->customData);
            }
            free (data);
            return NULL;
        }
        if (newIcon (&data->iconList[5], 78, 373, (numberEntries > 4 ? ICON_ENABLED : ICON_DISABLED),
                     career5, "spr/icon_book.png", "spr/icon_book_d.png") == 0)
        {
            deleteNIcons (data->iconList, 5);
            if (data->customData != NULL)
            {
                free (((CAREER_DATA*) data->customData)->careersPtr);
                free (data->customData);
            }
            free (data);
            return NULL;
        }

        data->background = loadSprite  ("spr/bg_careers.png");
        if (data->background == NULL)
        {
            if (data->customData != NULL)
            {
                free (((CAREER_DATA*) data->customData)->careersPtr);
                free (data->customData);
            }
            deleteNIcons (data->iconList, 6);
            free (data);
            fprintf (stderr, "%s\n", SDL_GetError());
            error = EXIT_FAILURE;
            return NULL;
        }
        data->bgPosition.x = 0;
        data->bgPosition.y = 0;
    }
    else
    {
        fprintf (stderr, outOfMemory);
        error = EXIT_FAILURE;
    }
    return data;
}


1533

Pour la première partie, tu pourrais ne pas imbriquer tous les tests : data = malloc(...); if (!data) { return NULL; } scoresFile = fopen (...); if (!scoresFile) { free(data); return NULL; } fread(...); if (feof (scoresFile)) { fclose(scoreFile); free(data); return NULL; } ...
Tu peux mettre des goto vers la fin de la fonction pour une gestion des erreurs plus concise.

Pour la seconde partie, tu devrais utiliser une boucle ou une procédure puisque tu refais toujours la même chose smile
Ensuite, tu essayes peut-être de faire trop de choses dans une seule fonction. Tu ne pourrais pas séparer la lecture du fichier et la création du MODULE_DATA ?
avatar

1534

Tu peux simplifier en considérant que si une erreur de mémoire survient, alors tu peux arrêter tout ton programme plutôt que continuer l'erreur : le manque de mémoire devient une erreur terminatrice du programme, et donc plus besoin de gérer le retour NULL de malloc via ton my_malloc qui fait un abort si NULL est retourné par malloc.
Surtout qu'avec la tendance des OS a surréservé la mémoire (par exemple, Linux autorise à allouer plus de mémoires virtuelles qu'il ne peut en allouer physiquement), tu peux avoir un pointeur valide, mais dès que tu essayes d'y écrire, boum. Terminaison (pas forcément du tien d'ailleurs).
Un autre raison pour est que si ton programme n'arrive pas à allouer les 44 octets nécessaires à tes structures, il ne pourra rien faire du tout de toute facon.
Bon évidement, si c'est une grosse allocation, cette hypothèse ne marche pas.

1535

Tiens, il manque des tests pour savoir si les fread ont réussi smile
avatar

1536

Pour les erreurs fatales (qui font que tu vas terminer ton programme), atexit() est une solution assez élégante.
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

1537

Wow quelle avalanche de conseils, merci beaucoup \o/
Thepro (./1533) :
Pour la première partie, tu pourrais ne pas imbriquer tous les tests :

Je sais pas pourquoi je suis parti sur cette idée. Je vais réécrire tout ce fichier. D'ailleurs, c'est pour ça que je termine jamais un programme, je recommence tout quand ça va pas.
Thepro (./1533) :
Ensuite, tu essayes peut-être de faire trop de choses dans une seule fonction. Tu ne pourrais pas séparer la lecture du fichier et la création du MODULE_DATA ?

Pourquoi pas, mais ça serait purement "artificiel". Je construit ici un module (la page des highscores) qui a bien besoin de lire ce fichier-là pour se construire. Mais si ça fait plus propre, pourquoi pas appeler une fonction inline.
PpHd (./1534) :
Linux autorise à allouer plus de mémoires virtuelles qu'il ne peut en allouer physiquement), tu peux avoir un pointeur valide, mais dès que tu essayes d'y écrire, boum

J'ai vu ça dans les doc de malloc cette semain, j'ai halluciné eek Mais j'ai encore plus halluciné quand Kevin m'a dit que les programmes ne vérifiaient pas, la majorité du temps, leurs malloc, que std::bad_alloc n'était jamais intercepté etc... Dans tout mon programme, je termine proprement en cas d'erreur. Bonne habitude prise sur TI.
Toi tu me conseilles de faire un abort si j'ai quelque chose qui échoue ? C'est-à-dire je ne libère rien, je laisse tout en plan et je me casse ?
Je trouve ça super crade grin

ps -> ah oué, tu me conseilles de définir une fonction void* my_alloc(size) qui fait abort si elle a foiré
PpHd (./1534) :
Terminaison (pas forcément du tien d'ailleurs).

OOM Killer. Je trouve ça bizare ce truc, dans quel état le système se retrouve après ça ^^ Mais c'est sûr que c'est mieux qu'un système sans mémoire.
(J'ai vu ça dans la doc aussi, sisi Bob cheeky)
PpHd (./1534) :
Un autre raison pour est que si ton programme n'arrive pas à allouer les 44 octets nécessaires à tes structures, il ne pourra rien faire du tout de toute facon.

Oui bien sûr, le système est évidemment dans un état critique. Ya peut-être autre chose à faire que de nettoyer tout proprement. Je sais pas quelles sont les bonnes stratégies sur PC. Sur TI en tout cas, j'ai toujours fait proprement (même si PreOS aurait pu tout faire pour moi #itm#).
Thepro (./1535) :
Tiens, il manque des tests pour savoir si les fread ont réussi

Oui, rajoutés depuis, bien vu t'as l'oeil grin (feof et ferror)
Zerosquare (./1536) :
Pour les erreurs fatales (qui font que tu vas terminer ton programme), atexit() est une solution assez élégante.

Effectivement, j'ai enregisté deux fonctions au début de mon programme : une qui ferme la STL, une autre qui détruit les modules chargés en appelant leur destructeurs (qui eux-même appellent ceux des objets qui les composent, c'est bien foutu je suis content de mon système trilove).
Mais je crois qu'un appel à abort zappe les atexit() sad

1538

Folco (./1537) :
ps -> ah oué, tu me conseilles de définir une fonction void* my_alloc(size) qui fait abort si elle a foiré

Le fameux xmalloc. Solution très très courante. Mais en général c'est un exit(1); qu'on fait (ou un exit avec un code d'erreur spécial), pas un abort();. abort() est intercepté par les divers intercepteurs de plantages, ça passe pour un bogue du logiciel alors que c'est juste la mémoire qui était pleine.
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

1539

Folco (./1537) :
Mais je crois qu'un appel à abort zappe les atexit() sad

Raison de plus d'utiliser exit, pas abort.
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

1540

Folco (./1537) :
Oui, rajoutés depuis, bien vu t'as l'oeil biggrin.gif (feof et ferror)
Pourquoi feof ou ferror ? Tu peux tester si fread a lu le bon nombre d'éléments. La valeur retournée doit être la même que celle entrée en 3e paramètre si tout s'est bien passé smile
avatar

1541

Folco (./1537) :
Je sais pas quelles sont les bonnes stratégies sur PC. Sur TI en tout cas, j'ai toujours fait proprement (même si PreOS aurait pu tout faire pour moi #itm#).

Tout dépend de ce que tu es en train de faire, mais pour ma part:
[ul][li]TI: obligé de libérer proprement, un crash passera vraiment mal. CHIANT mais on fait des petits progs donc ça va.[/li]
[li]PC, utilitaire: osef total -> crash par lecture/écriture proche de NULL, l'OS affiche "ce programme a cessé de fonctionner" et l'utilisateur se doute bien qu'il joue avec le feu (config trop faible, trop de trucs ouverts), il n'a qu'à réessayer.[/li]
[li]PC, critique: exit() en cas d'erreur durant la phase de chargement, vérifications propres ensuite comme tu le fais à l'intérieur du programme[/li]
[li]Console, faible: utilisation d'ASSERT, une macro qui éteint la console si la condition est fausse. Ne sert que pour le dév, une erreur ne doit pas arriver (allocations statiques +pool géré en LIFO au besoin).[/li][/ul]
Autre solution pas jolie mais simple:int fonctionChargement() { FILE *res1 = fopen(...); int *res2 = 0L; if (!res1) goto error; res2 = malloc(...); if (!res2) goto error; ... return 0; error: if (res1) fclose(res1); if (res2) free(res2); return 1; }
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

1542

Bon, j'ai réécrit la première partie de la fonction (lecture du fichier de highscores) en suivant tout vos conseils (sauf en ce qui concerne la gestion de l'exit, j'arrive pas à me faire à l'idée de ne pas vérifier une allocation tsss). J'ai donc viré la structure des if imbriqués et utilisés des labels (première fois de ma vie, notez l'effort grin). J'ai aussi utilisé les vérifications de fread indiquées par Thepro, c'est vraiement beaucoup plus simple en effet.

En tout cas, mon code devient, je pense, beaucoup lisible, simple et clair :
refactoring
    MODULE_DATA* data;
    uint16_t numberEntries = 0;
    FILE* scoresFile;
    const char* errorStr;

    data = malloc (sizeof (MODULE_DATA));                   /* Out of memory */
    if (data == NULL)
        return NULL;

    scoresFile = fopen ("careers", "rb");                   /* File doesn't exist, just skip */
    if (scoresFile == NULL)
        goto NoEntry;                                       
    
    errorStr = fileCareersInvalid;                          /* Invalid file format, don't use it */
    if ((fread (&numberEntries, sizeof (uint16_t), 1, scoresFile) != sizeof (uint16_t)) || (numberEntries > 5))
        goto CloseFile;                                     
    
    errorStr = outOfMemory;                                 /* Out of memory */
    data->customData = malloc (sizeof (CAREER_DATA))
    if (data->customData == NULL)
        goto CloseFile;                                     
    
    ((CAREER_DATA*) data->customData)->numEntries;
    ((CAREER_DATA*) data->customData)->careersPtr = malloc (numberEntries * sizeof (ENTRY_CAREER));
    
    errorStr = outOfMemory;                                 /* Out of memory */
    if (((CAREER_DATA*) data->customData)->careersPtr == NULL)
        goto FreeCustomData;                                     
    
    errorStr = fileCareersInvalid;                          /* Invalid file format, don't use it */
    if (fread (((CAREER_DATA*) data->customData)->careersPtr, sizeof (ENTRY_CAREER), numberEntries, scoresFile) == sizeof (ENTRY_CAREER))
        goto Success

    free (((CAREER_DATA*) data->customData)->careersPtr);        
FreeCustomData:
    free (data->customData);
CloseFile:
    fprintf (stderr, errorStr);
    fclose (scoresFile);
NoEntry:
    data->customData = NULL;
    numberEntries = 0;

Aux courageux qui regarderont encore, nhésitez pas à critiquer, merci d'avance. smile

Zerosquare (./1536) :
Pour les erreurs fatales (qui font que tu vas terminer ton programme), atexit() est une solution assez élégante.

J'ai vu ça ici : http://pgdc.purdue.org/sdltutorial/01/
Et quand j'ai fait un man atexit, j'ai encore hurlé : atexit peut échouer (par manque de mémoire j'imagine ? Posix en garantie/impose 32 minimum), et le code ne vérifie pas. Je le fais dans mon programme, c'est encore plus élégant. Je n'ai évidemment aucune leçon à donner, mais je ne comprends pas pourquoi je vois autant de code que je considère crade. Je me rends compte qu'un des avantages de l'assembleur sur TI est que ça impose une grande rigueur.

1543

Tu as l'art de troller toi, et le pire c'est que ça marche, on y réagit tongue
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

1544

#mazza#

1545

Et voilà une version complètement réécrite. C'est 100 fois mieux, merci beaucoup. smile
code
/** loadCareers
  *
  * Initialize the Careers module. First read the save file, then create the interface.
  *
  * Return a pointer to itself, or NULL if something failed.
  **/

MODULE_DATA* loadCareers()
{
    MODULE_DATA* data;
    uint16_t numberEntries;
    FILE* scoresFile;
    const char* errorStr;

    data = malloc (sizeof (MODULE_DATA));                   /* Out of memory */
    if (data == NULL)
        return NULL;

/** Open file and read it **/
    scoresFile = fopen ("careers", "rb");                   /* File doesn't exist, just skip */
    if (scoresFile == NULL)
        goto NoEntry;

    errorStr = fileCareersInvalid;                          /* Invalid file format, don't use it */
    if ((fread (&numberEntries, sizeof (uint16_t), 1, scoresFile) != sizeof (uint16_t)) || (numberEntries > 5))
        goto CloseFile;

    errorStr = outOfMemory;                                 /* Out of memory */
    data->customData = malloc (sizeof (CAREER_DATA));
    if (data->customData == NULL)
        goto CloseFile;

    ((CAREER_DATA*) data->customData)->numEntries = numberEntries;
    ((CAREER_DATA*) data->customData)->careersPtr = malloc (numberEntries * sizeof (ENTRY_CAREER));

    errorStr = outOfMemory;                                 /* Out of memory */
    if (((CAREER_DATA*) data->customData)->careersPtr == NULL)
        goto FreeCustomData;

    errorStr = fileCareersInvalid;                          /* Invalid file format, don't use it */
    if (fread (((CAREER_DATA*) data->customData)->careersPtr, sizeof (ENTRY_CAREER), numberEntries, scoresFile) == sizeof (ENTRY_CAREER))
        goto Success;

    free (((CAREER_DATA*) data->customData)->careersPtr);
FreeCustomData:
    free (data->customData);
CloseFile:
    fprintf (stderr, errorStr);
    fclose (scoresFile);
NoEntry:
    data->customData = NULL;
    numberEntries = 0;

    /** Create interface **/
Success:

    data->numIcons = 0;
    data->iconList = malloc (7 * sizeof(ICON));

    if (data->iconList == NULL)
    {
        fprintf (stderr, outOfMemory);
        goto FreeAll;
    }
    
    if (newIcon (&data->iconList[0], 521, 413, ICON_ENABLED, comeBack, "spr/icon_valid.png", NULL) == 0)
        goto FreeAll;
    data->numIcons ++;
    
    if (newIcon (&data->iconList[1], 78, 237, (numberEntries > 0 ? ICON_ENABLED : ICON_DISABLED),
                 career1, "spr/icon_book.png", "spr/icon_book_d.png") == 0)
        goto FreeAll;
    data->numIcons ++;
    
    if (newIcon (&data->iconList[2], 78, 271, (numberEntries > 1 ? ICON_ENABLED : ICON_DISABLED),
                 career2, "spr/icon_book.png", "spr/icon_book_d.png") == 0)
        goto FreeAll;
    data->numIcons ++;
    
    if (newIcon (&data->iconList[3], 78, 305, (numberEntries > 2 ? ICON_ENABLED : ICON_DISABLED),
                 career3, "spr/icon_book.png", "spr/icon_book_d.png") == 0)
        goto FreeAll;
    data->numIcons ++;
    
    if (newIcon (&data->iconList[4], 78, 339, (numberEntries > 3 ? ICON_ENABLED : ICON_DISABLED),
                 career4, "spr/icon_book.png", "spr/icon_book_d.png") == 0)
        goto FreeAll;
    data->numIcons ++;
    
    if (newIcon (&data->iconList[5], 78, 373, (numberEntries > 4 ? ICON_ENABLED : ICON_DISABLED),
                 career5, "spr/icon_book.png", "spr/icon_book_d.png") == 0)
        goto FreeAll;
    data->numIcons ++;

    data->background = loadSprite  ("spr/bg_careers.png");
    if (data->background == NULL)
        goto FreeAll;
    data->bgPosition.x = 0;
    data->bgPosition.y = 0;
    return data;


FreeAll:
    if (data->customData != NULL)
    {
        free (((CAREER_DATA*) data->customData)->careersPtr);
        free (data->customData);
    }
    if (data->iconList != NULL)
    {
        deleteNIcons(data->iconList, data->numIcons);
        free (data->iconList);
    }
    free (data);
    error = EXIT_FAILURE;
    return NULL;
}

1546

./1543 > Il a un bon professeur #modgic#
avatar
Le scénario de notre univers a été rédigée par un bataillon de singes savants. Tout s'explique enfin.
T'as un problème ? Tu veux un bonbon ?
[CrystalMPQ] C# MPQ Library/Tools - [CrystalBoy] C# GB Emulator - [Monoxide] C# OSX library - M68k Opcodes

1547

Folco (./1545) :
if ((fread (&numberEntries, sizeof (uint16_t), 1, scoresFile) != sizeof (uint16_t)) || (numberEntries > 5))
Ça ne va pas fonctionner, car fread renvoie le nombre d'éléments lus et non la taille de ce qui est lu... Il faut donc vérifier que 1 (3e paramètre) est renvoyé plutôt que sizeof(uint16_t) smile
if (fread (((CAREER_DATA*) data->customData)->careersPtr, sizeof (ENTRY_CAREER), numberEntries, scoresFile) == sizeof (ENTRY_CAREER))
Ici, tu as oublié de multiplier ce que tu compares à la sortie par numberEntries... Mais ce n'est pas bon, comme précédemment, il faut seulement comparer avec numberEntries smile
avatar

1548

Ok, j'ai inversé ce que t'as dit, merci d'avoir vérifié grin Etape suivante : écrire un fichier de ce type pour voir si tout marche bien. smile

1549

GoldenCrystal (./1546) :
./1543 > Il a un bon professeur #modgic#

pencil grin l'air de pas y toucher toussah tongue
Mais après j'en voient qui se plaignent des leaks de Fx tout en conseillant de pas libérer la mémoire embarrassed

1550

(C'est qui le bon prof? tongue)
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

1551

je vous laisse troller sur qui est le meilleur prof de troll cheeky En tout cas, la compet' est rude, pas de souci grin

bon, question que je me pose depuis hier. Je vais probablement le savoir avec le programme que je viens d'écrire pour tester ma fonction (un programme qui va juste faire un fwrite pour écrire le fichier que mon programme principal devra lire).
Ma structure est donc majoritairement composée de uint16_t, donc d'entiers non signés sur 16 bits.
Je fais un fread d'un certains nombre de ces types de données, les octets sont donc lus consécutivement.
Ceci dit, j'ai déjà lu que la représentation mémoire d'une structure n'est jamais garantie. Qu'est-ce que m'assure alors qu'un fread de mon fichier vers ma structure en mémoire sera valide ? Pourquoi l'OS (32 ou 64 bits) n'alignerait pas mes uint16_t sur des blocs de 4 octets par exemple ? Vu ma manière de lire, je dois miser sur le fait que mes entiers vont être codés de manière consécutive en mémoire. Est-ce toujours vrai ?

Même si ça marche par hasard chez moi, est-ce que c'est garanti pour toutes les implémentations ?

Si ce n'est pas le cas, je m'y prend peut-être mal, et je devrais alors instancier les structures désirées, puis faire des fread de chaque entier dans le fichier, afin de l'enregistrer explicitement dans le membre qui lui est réservé dans la structure.

J'espère tout de même me faire des noeuds au cerveau pour rien grin

1552

Folco (./1551) :
Même si ça marche par hasard chez moi, est-ce que c'est garanti pour toutes les implémentations ?
Absolument pas. Lecture/écriture d'une structure sur disque en C -> conversion de chaque membre à la main de/vers une forme binaire indépendante de la plateforme (tu choisis little-endian ou big-endian, et tu t'y tiens). Dans d'autres langages, il y a des fonctions qui s'occupent de ça automatiquement (ça s'appelle la sérialisation), mais pas en C.
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

1553

Folco (./1551) :
J'espère tout de même me faire des noeuds au cerveau

Et il aime ça en plus tongue
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

1554

./1552 > pencil
À noter que la serialisation n'est vraiment utile que pour stocker des données internes au programme (i.e. c'est rarement un bon moyen d'échange de données) donc là ça t'aurait été utile.
En l'occurrence ça te fait un petit peu plus de code à écrire, mais en contrepartie, tu gardes la totale maîtrise du format de fichier.
(Après tu n'est pas non plus obligé de gérer l'endianness si tu juges que les fichiers ne sont pas sensés transiter d'une machine à une autre)
avatar
Le scénario de notre univers a été rédigée par un bataillon de singes savants. Tout s'explique enfin.
T'as un problème ? Tu veux un bonbon ?
[CrystalMPQ] C# MPQ Library/Tools - [CrystalBoy] C# GB Emulator - [Monoxide] C# OSX library - M68k Opcodes

1555

Zerosquare (./1552) :
Absolument pas. Lecture/écriture d'une structure sur disque en C -> conversion de chaque membre à la main de/vers une forme binaire indépendante de la plateforme (tu choisis little-endian ou big-endian, et tu t'y tiens).

Eh merde. Mon but est qu'un fichier de highscores soit portable : idéalement, je veux l'utiliser à partir d'un de mes Linux 32, 64 bits, ou même de mon Win XP.
Donc si je comprends bien, c'est mort !

La solution que je vois : écrire octet par octet (j'ai un tableau de 20 chars et 17 uint16) dans mon fichier, donc en décomposant les words en bytes, pas le choix ? Découpage, écriture (fputc je crois, je vais regarder man), et pour lire le fichier fgetc + reconstruction des words ?
A ce moment-là, je devrais être indépendant autant de la représentation mémoire de ma structure que de l'endianness, non ?
Brunni (./1553) :
Et il aime ça en plus

Parce qu'au moins, je me retrouverais pas, comme c'est apparemment le cas, avec une autre solution à écrire sad


Ah ben cross. Précisément, je veux que mon fichier puisse passer d'un linux 32 bits à un Windows 64 bits (en soi j'en ai pas besoin, mais je veux le faire de cette manière quoi).

1556

ZS> Même pas nécessaire d'utiliser la sérialisation en fait, Java par exemple cache tout seul l'endianness au niveau des streams wink
Folco> Solution possible: une interface avec des putByte, putShort, etc. que tu implémenteras une fois pour chacun des OS. Et décomposer ton écriture champ par champ (et il ne faut pas oublier de mettre à jour si tu modifies quelque chose). Tu peux aussi le faire générique dans une certaine mesure avec des sizeof et en jouant avec des décalages. C'est peut être pas un mauvais entrainement au final, essaie tongue
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

1557

Folco (./1555) :
La solution que je vois : écrire octet par octet (j'ai un tableau de 20 chars et 17 uint16) dans mon fichier, donc en décomposant les words en bytes, pas le choix ? Découpage, écriture (fputc je crois, je vais regarder man), et pour lire le fichier fgetc + reconstruction des words ? A ce moment-là, je devrais être indépendant autant de la représentation mémoire de ma structure que de l'endianness, non ?
Tout à fait.

Perso j'utilise la solution n°2 de Brunni, plus précisément la version générique avec décalage de bits.
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

1558

Brunni (./1556) :
Solution compliquée: une interface avec des putByte, putShort, etc. que tu implémenteras une fois pour chacun des OS.
trifus
L'intérêt de ces méthodes n'est pas justement de ne pas avoir à les implémenter plusieurs fois ?BOOL write_int32(FILE* f, INT32 v) { UINT8 buffer[4]; buffer[0] = v & 0xFF; buffer[1] = (v >> 8) & 0xFF; buffer[2] = (v >> 16) & 0xFF; buffer[3] = (v >> 32) & 0xFF; return fwrite(buffer, 4 * sizeof(UINT8), 1, f) == 1; }
avatar
Le scénario de notre univers a été rédigée par un bataillon de singes savants. Tout s'explique enfin.
T'as un problème ? Tu veux un bonbon ?
[CrystalMPQ] C# MPQ Library/Tools - [CrystalBoy] C# GB Emulator - [Monoxide] C# OSX library - M68k Opcodes

1559

Si tu peux t'assurer que ton int est bien 32 bits ton code marche*, mais rien de tout ça n'est garanti en C(++).
Je peux savoir pourquoi tu n'as pas choisi l'endianness de ta machine préférée au fait? tongue
* en fait il marche pas non plus, mais t'en es pas loin
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

1560

Brunni (./1556) :
que tu implémenteras une fois pour chacun des OS

Je ne comprends pas pourquoi, le C ne me permet pas de faire ça de manière portable justment, si je me limite à la lecture/écriture de chars et aux types de stdint.h ? En tout cas, c'est pour ça que je suis allé chercher ce header.
Brunni (./1556) :
Et décomposer ton écriture champ par champ (et il ne faut pas oublier de mettre à jour si tu modifies quelque chose)

Oui, c'est chiant mais je crois que je n'ai pas le choix.

Et merci pour tout tout le monde. smile

(multi-cross)