Posté le 11/11/2014 à 11:03 Membre depuis le 30/06/2001, 70810 messages
C'est surtout la syntaxe qui est un peu moisie..
avatarProud to be CAKE©®™
The cake is a lie! - Love your weighted companion cube

->986-Studio's Wonder Project!<-
yN a cassé ma signature :o
Posté le 11/11/2014 à 11:10 Membre depuis le 16/06/2001, 69242 messages
Non le truc de RHJPP est sale, le fonctionnement de printf c'est FORMAT(obligatoire)+ARGS(eventuellement vide), pas ARGS(eventuellement vide)+""

Pas besoin de "truc" quand la methode officielle existe grin
Posté le 11/11/2014 à 11:22Edité par PpHd le 11/11/2014 à 12:55 Membre depuis le 11/06/2001, 19563 messages
En utilisant la bibliothèque P99 (flemme de devoir tout réécrire !) :

#include "p99_if.h"

#define CHECK(condition, label, ...)                                    \
    if (!(condition)) {                                                 \
        fprintf (stderr, "%s, %s, %i\n", __FILE__, __func__, __LINE__); \
        P99_IF_EMPTY(__VA_ARGS__) () (fprintf(stderr, __VA_ARGS__);)    \
        goto label;                                                     \
    }  

CHECK(1, error);
CHECK(1, error, "Data is %d\n", 2);


donne bien:

if (!(1)) { fprintf (stderr, "%s, %s, %i\n", "test.c", __func__, 12); goto error; };

if (!(1)) { fprintf (stderr, "%s, %s, %i\n", "test.c", __func__, 14); fprintf(stderr, "Data is %d\n", 2); goto error; };


Elle est dispo là: http://p99.gforge.inria.fr/

(pour info, c'est 100% standard compliant).
Posté le 11/11/2014 à 11:28 Membre depuis le 18/06/2001, -26239 message
squalyl (./61) :
Non le truc de RHJPP est sale, le fonctionnement de printf c'est FORMAT(obligatoire)+ARGS(eventuellement vide), pas ARGS(eventuellement vide)+""

Pas besoin de "truc" quand la methode officielle existe grin.gif?14

Ok, mais c'est précisément à la macro que je confie la merde.
Ce qui m'intéresse, c'est de pouvoir faire des appels avec ou sans chaine de caractère. Et en l'occurence, le truc de RHJPP donne un format vide et pas d'argument derrière, où est le problème ? Je dois simplement passer -Wno-format-zero-length parce que printf prévient si on passe une chaine vide.

PpHd -> Merci, je regarde, ça a l'air chouette cette lib. smile
avatar<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !
Posté le 11/11/2014 à 11:32 Membre depuis le 16/06/2001, 69242 messages
pour moi le fait de devoir ajouter un -Wkekchoz n'est pas satisfaisant smile

Apres chacun fait comme il veut smile
Posté le 11/11/2014 à 11:39 Membre depuis le 18/06/2001, -26239 message
Bon, j'avoue ma mauvaise foi, "ça me va" parce que "j'ai rien d'autre" grin
En fait, le warning ici est fait non pour signaler une construction dangereuse genre if (a = b), mais amha pour signaler une erreur d'innatention ("attention mec, t'as rien dans ta string, ya pas un souci ?"). Donc on est pas du tout dans le même degré de craditude. embarrassed
avatar<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !
Posté le 11/11/2014 à 11:44 Membre depuis le 30/06/2001, 70810 messages
squalyl (./64) :
pour moi le fait de devoir ajouter un -Wkekchoz n'est pas satisfaisant smile

Apres chacun fait comme il veut smile


Si, chaque compilateur devrait etre en -Wall -Werror par defaut embarrassed
avatarProud to be CAKE©®™
The cake is a lie! - Love your weighted companion cube

->986-Studio's Wonder Project!<-
yN a cassé ma signature :o
Posté le 11/11/2014 à 11:46 Membre depuis le 11/07/2003, 54606 messages
Mais ça ne suffit pas embarrassed
Comme l'indique bien son nom, -Wall n'active pas tous les warnings. -Wextra non plus, d'ailleurs sad
avatar<<< Kernel Extremis©®™ >>> et Inventeur de la différence administratif/judiciaire ! (©Yoshi Noir)

<Vertyos> un poil plus mais elle suce bien quand même la mienne ^^
<Sabrina`> tinkiete flan c juste qu'ils sont jaloux que je te trouve aussi appétissant
Posté le 11/11/2014 à 11:48 Membre depuis le 30/06/2001, 70810 messages
C'est vrai, mais c'est mieux que le comportement par defaut embarrassed
avatarProud to be CAKE©®™
The cake is a lie! - Love your weighted companion cube

->986-Studio's Wonder Project!<-
yN a cassé ma signature :o
Posté le 11/11/2014 à 11:49 Membre depuis le 04/05/2005, 4416 messages
Si tu ne veux pas de warning, suffit que la chaine ne soit pas vide ^^
#define CHECK(condition, label, ...)                                \
  if (!(condition)) {                                               \
    fprintf(stderr, "%s, %s, %i\n", __FILE__, __func__, __LINE__);  \
    if (*#__VA_ARGS__)                                              \
      fprintf(stderr, ##__VA_ARGS__, "pas vide");                   \
    goto label;                                                     \
  }


C'est sûr que ce n'est pas parfaitement propre et il y a un risque de non-détection du manque d'un argument chaine de caractère. S'il y a un %s en trop dans le format, ça ne sera pas détecté et ça affichera pas vide...
À voir si c'est acceptable pour Folco. J'ai donné cette solution parce qu'elle fonctionne et est rapide à mettre en place (surtout à 3 heures cheeky).
avatar
Posté le 11/11/2014 à 12:27 Membre depuis le 18/06/2001, -26239 message
Non, ça ne va pas, "pas vide" sera affiché à chaque fois que j'appelle la macro sans chaine de caractère, non ? du coup ça ferait super crade ^^

Et j'ai écrit ça, c'est plus explicite, et c'est plus lisible en fonction des cas :
#define IF_TRUE(condition, label, ...)                                  \
    if (condition) {                                                    \
        fprintf (stderr, ##__VA_ARGS__, "");                            \
        fprintf (stderr, "%s, %s, %i\n", __FILE__, __func__, __LINE__); \
        goto label;                                                     \
    }

#define IF_FALSE(condition, label, ...) IF_TRUE (!(condition), label, ##__VA_ARGS__)
avatar<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !
Posté le 11/11/2014 à 12:30 Membre depuis le 04/05/2005, 4416 messages
Folco (./70) :
Non, ça ne va pas, "pas vide" sera affiché à chaque fois que j'appelle la macro sans chaine de caractère, non ? du coup ça ferait super crade ^^
Non, il y a if (*#__VA_ARGS__) pour gérer ce cas wink
avatar
Posté le 11/11/2014 à 12:34 Membre depuis le 18/06/2001, -26239 message
Ah yep, j'ai lu trop vite, bien vu merci. smile
flanker (./67) :
Mais ça ne suffit pas embarrassed
Comme l'indique bien son nom, -Wall n'active pas tous les warnings. -Wextra non plus, d'ailleurs sad

Tiens, je découvre que -Wextra n'est autre que -W, si je comprends bien la doc, mais que -W serait deprecated : https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
avatar<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !
Posté le 11/11/2014 à 12:52 Membre depuis le 16/06/2001, 69242 messages
./66 c'est bien les seuls acceptables embarrassed
Posté le 11/11/2014 à 16:08 Membre depuis le 18/06/2001, -26239 message
Bon, donc au final, j'ai ces macros :
#define IF_TRUE(condition, label, ...)                                  \
    if (condition) {                                                    \
        if (*#__VA_ARGS__) {                                            \
            fprintf (stderr, ##__VA_ARGS__, "");                        \
            fprintf (stderr, "\n");                                     \
        }                                                               \
        fprintf (stderr, "%s, %s, %i\n", __FILE__, __func__, __LINE__); \
        goto label;                                                     \
    }

#define IF_FALSE(condition, label, ...) IF_TRUE (!(condition), label, ##__VA_ARGS__)

Et ça donne des choses de ce genre :
int main (void)
{
    // Initialization
    int ret = EXIT_FAILURE;
    IF_FALSE (InitSdl (), SdlFail)
    IF_FALSE (InitFont (), FontFail)
    IF_FALSE (InitRessources(), RessourcesFail)
    IF_FALSE (InitGameState(), GameStateFail)

    // Game engine
    IF_FALSE (GameStateEngine(), EngineFail)
    ret = EXIT_SUCCESS;

    // Cleanup
EngineFail:
    DeinitGameState ();
GameStateFail:
    DeinitRessources ();
RessourcesFail:
    DeinitFont ();
FontFail:
    DeinitSdl ();
SdlFail:

    return ret;
}

Le IF_FALSE/TRUE est limite trop explicite, mais c'est bien plus simple à lire que IF/IFN (pour moi).
avatar<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !
Posté le 12/11/2014 à 15:02Edité par Folco le 12/11/2014 à 15:20 Membre depuis le 18/06/2001, -26239 message
, label, ##__VA_ARGS__)
Bon, vala maintenant : #define IF_TRUE(condition, label, ...)                                             \
    if (condition) {                                                               \
        if (*#__VA_ARGS__) {                                                       \
            fprintf (stderr, "ERROR: ");                                           \
            fprintf (stderr, ##__VA_ARGS__, "");                                   \
            fprintf (stderr, "\n");                                                \
        }                                                                          \
        fprintf (stderr, "TRACE: %s:%i, in %s()\n", __FILE__, __LINE__, __func__); \
        goto label;                                                                \
    }

#define IF_FALSE(condition, label, ...) IF_TRUE (!(condition)

Ca donne des trucs comme ça :
folco@Foch:~/fpgc/Debug$ ./fpgc
ERROR: Couldn't open ../spr/ButtonExit_out.png
TRACE: /home/folco/fpgc/src/Ressources.c:18, in LoadSpriteFile()
TRACE: /home/folco/fpgc/src/Ressources.c:32, in InitRessources()
TRACE: /home/folco/fpgc/src/main.c:15, in main()
folco@Foch:~/fpgc/Debug$ 

Par contre, je dois passer -Wno-format-extra-args pour éviter un warning, dans le cas où je fournis une chaine avec des donénes à formatter, genre IF_TRUE (m_count == GS_COUNT_MAX, Exit, "ERROR: max game state reached, %i", GS_COUNT_MAX), parce qu'il n'y a que %i à formater, et gcc voit GS_COUNT_MAX et le "" de la macro. J'ai pas trouvé comment éviter ça (sans utiliser une lib telle celle proposée par PpHd).
avatar<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !
Posté le 12/11/2014 à 15:16 Membre depuis le 27/04/2006, 59488 messages
top
avatarZeroblog

« 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
Posté le 12/11/2014 à 17:22 Membre depuis le 18/06/2001, -26239 message
C'est vraiment génial ce truc, merci beaucoup pour votre aide, parce que à l'instant, j'ai eu ça :
$ ./fpgc 
TRACE: /home/folco/fpgc/src/GameStates.c:130, in GameStateEngine()
TRACE: /home/folco/fpgc/src/main.c:19, in main()

Et en fait, ya pas d'erreur, juste une faute de logique à cette ligne, qui fait que ça quitte alors que ça devrait pas. Sans vérification des retours, je sais pas comment j'aurais fait pour trouver, j'aurais du tracer et je me serais bien fait chier grin
En tout cas, maintenant, ça donne du code de ce genre, et c'est bien plus simple à lire, et certainement plus facile à déboguer. smile
avatar<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !
Posté le 12/11/2014 à 17:47 Membre depuis le 27/04/2006, 59488 messages
Bug report et patch embarrassed :
-// This function does nothing else returning true, and allows a game state to ignore some callbacks
+// This function does nothing besides returning true, and allows a game state to ignore some callbacks
avatarZeroblog

« 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
Posté le 12/11/2014 à 18:04 Membre depuis le 18/06/2001, -26239 message
MERCI !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Par contre, c'est un patch unifié ou pas ? Je sais pas trop comment le déployer, je vais en parler avec les responsables de mes équipes de dev lundi matin. embarrassed
avatar<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !
Posté le 12/11/2014 à 18:12 Membre depuis le 27/04/2006, 59488 messages
hehe
avatarZeroblog

« 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
Posté le 12/11/2014 à 18:15 Membre depuis le 30/06/2001, 70810 messages
Si tu veux ameliorer ta macro, tu peux utiliser backtrace() pour savoir exactement l'ordre d'appel pour savoir comment tu en es arrive la smile

http://man7.org/linux/man-pages/man3/backtrace.3.html

Tu dois meme pouvoir mettre une fonction qui joue le role de breakpoint pour un debugger smile
avatarProud to be CAKE©®™
The cake is a lie! - Love your weighted companion cube

->986-Studio's Wonder Project!<-
yN a cassé ma signature :o
Posté le 12/11/2014 à 18:20 Membre depuis le 18/06/2001, -26239 message
C'est chouette cette fonction, merci beaucoup ! grin

(eh oui, quand on code pas bien, on apprend tous les moyens pour déboguer, c'est comme ça grin)
avatar<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !
Posté le 12/11/2014 à 18:24 Membre depuis le 30/06/2001, 70810 messages
Par contre attention backtrace est moyennement portable, il y a des plateforme ou il ne fait rien.. :/
avatarProud to be CAKE©®™
The cake is a lie! - Love your weighted companion cube

->986-Studio's Wonder Project!<-
yN a cassé ma signature :o
Posté le 12/11/2014 à 18:42 Membre depuis le 11/06/2001, 19563 messages
Folco (./75) :
J'ai pas trouvé comment éviter ça (sans utiliser une lib telle celle proposée par PpHd).


C'est juste des .h, rien d'autres. Tu peux récupérer juste ce que tu veux.
Posté le 12/11/2014 à 19:42 Membre depuis le 04/05/2005, 4416 messages
Folco (./75) :
Par contre, je dois passer -Wno-format-extra-args pour éviter un warning, dans le cas où je fournis une chaine avec des donénes à formatter, genre IF_TRUE (m_count == GS_COUNT_MAX, Exit, "ERROR: max game state reached, %i", GS_COUNT_MAX), parce qu'il n'y a que %i à formater, et gcc voit GS_COUNT_MAX et le "" de la macro. J'ai pas trouvé comment éviter ça (sans utiliser une lib telle celle proposée par PpHd).

Pourquoi ne pas continuer avec le bricolage ? grin
#define SP_FPRINTF(file, format, ...)                                              \
    if (*#__VA_ARGS__) {                                                           \
        const char *f = format;                                                    \
        size_t l = strlen(f);                                                      \
        char f2[l + 3];                                                            \
        strcpy (f2, f);                                                            \
        strcpy (f2 + l, "%s");                                                     \
        fprintf (file, f2, ##__VA_ARGS__);                                         \
    } else {                                                                       \
        fprintf (file, format);                                                    \
    }

#define IF_TRUE(condition, label, ...)                                             \
    if (condition) {                                                               \
        if (*#__VA_ARGS__) {                                                       \
            fprintf (stderr, "ERROR: ");                                           \
            SP_FPRINTF (stderr, ##__VA_ARGS__, "\n");                              \
        }                                                                          \
        fprintf (stderr, "TRACE: %s:%i, in %s()\n", __FILE__, __LINE__, __func__); \
        goto label;                                                                \
    }
 
#define IF_FALSE(condition, label, ...) IF_TRUE (!(condition), label, ##__VA_ARGS__)


J'ai fait une marco, mais tu peux faire pareil en fonctionnel wink
avatar
Posté le 12/11/2014 à 20:17 Membre depuis le 18/06/2001, -26239 message
PpHd (./84) :
C'est juste des .h, rien d'autres. Tu peux récupérer juste ce que tu veux.

J'ai meme essayé de les comprendre, c'est certainement très puissant, mais les sources sont quand même vachement capillo-tractées, et j'ai pas compris comment ça marchait, donc j'utilise pas. On a ses principes, non mais. embarrassed

Merci RHJPP, je continuerai à explorer quand j'aurai résoudru des trucs plus merdiques à coté grin
avatar<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !
Posté le 12/11/2014 à 20:44 Membre depuis le 11/06/2001, 19563 messages
Folco (./86) :
J'ai meme essayé de les comprendre, c'est certainement très puissant, mais les sources sont quand même vachement capillo-tractées, et j'ai pas compris comment ça marchait, donc j'utilise pas. On a ses principes, non mais. embarrassed.gif?14


Ah mais la méta programmation en préprocesseur C n'est pas compliqué. C'est juste des couches, empilées dans des couches, empilées dans des couches, pour faire les substitutions que l'on veut.
Tout repose sur P99_HAS_COMMA qui retourne 0 ou 1 si l'argument qu'on lui donne en paramètre à une virgule.
Pour ce faire, on définit la macro comme étant:

#define P99_HAS_COMMA(...) P00_ARG(__VA_ARGS__,                \
 1, 1, 1, 1, 1, 1, 1,                                          \
...
 1, 1, 1, 1, 1, 1, 1, 1,                                       \
 1, 1, 1, 1, 1, 1, 0, 0)

P00_ARG retourne son 159ième argument.
ie. on utilise l'argument VA_ARGS qui va décaler ou pas les arguments donnés en paramètre à P00_ARG qui va retourner 1 ou 0 selon que l'on a une virgule ou pas.

Avec cette macro, on peut construire P99_IS_EMPTY:

#define P99_IS_EMPTY(...)                                                 \
P00_ISEMPTY(                                                              \
            /* test if there is just one argument, that might be empty */ \
             P99_HAS_COMMA(__VA_ARGS__),                                  \
             /* test if P99_IS__EQ__ together with the argument           \
                adds a comma */                                           \
             P99_HAS_COMMA(P00_IS__EQ__ __VA_ARGS__),                     \
             /* test if the argument together with a parenthesis          \
                adds a comma */                                           \
             P99_HAS_COMMA(__VA_ARGS__ (/*empty*/)),                      \
             /* test if placing it between P99_IS__EQ__ and the           \
                parenthesis adds a comma */                               \
             P99_HAS_COMMA(P00_IS__EQ__ __VA_ARGS__ (/*empty*/))          \
             )

Le point clé est le dernier argument : si __VA_ARGS__ est vide, on obtient : P00_IS__EQ__ (/*empty*/), qui reconnu comme un appel de macro, qui va injecter une virgule !
Donc l'argument est vide lorsque seul le dernier élément (P99_HAS_COMMA(P00_IS__EQ__ __VA_ARGS__ (/*empty*/)) ) est vrai et pas les autres !
Donc on évalue tous les arguments, qui ne valent que 1 ou 0. On concatène le tout (P00_ISEMPTY) et via une autre rangé de macro, on dit si oui ou non on a gagné:

#define P00_IS_EMPTY_CASE_0000 P00_IS_EMPTY_CASE_0000
#define P00_IS_EMPTY_CASE_0001 ,
#define P00_IS_EMPTY_CASE_0010 P00_IS_EMPTY_CASE_0010
#define P00_IS_EMPTY_CASE_0011 P00_IS_EMPTY_CASE_0011
....

Là, il réutilise sa macro IS_COMMA pour dire si oui ou non, c'est vide. J'aime beaucoup love

A partir de là tu as 0 ou 1 selon que c'est vide ou pas.
Tu redéfinis des macros

#define MACHIN_TO_KEEP_IF_1(...) 
#define MACHIN_TO_KEEP_IF_0(...) __VA_ARGS__

tu concatènes ta sortie de ta macro précédente avec MACHIN_TO_KEEP (avec le bon niveau de substitution) et si c'est pas vide, tu expanses ton argument.
En faisant simple, ca donne

#define KEEP_IF_NOT_EMPTY(...) MACHIN_TO_KEEP_ ## IS_EMPTY(__VA_ARGS__) (func(__VA_ARGS__))

(sauf que ca ne va pas marcher parque les substitutions ne se passeront pas au bon moment (## est prioritaire sur l'appel d'une macro !) Mais le principe est là.
Posté le 12/11/2014 à 21:25 Membre depuis le 18/06/2001, -26239 message
Merci beaucoup pour l'explication en détail. En fait, à la base, j'ai regardé comment ça marchait car je pensais trouver une méthode rigoureuse, je veux dire générique. Là, ça s'appuie sur un tableau contenant un nombre fixé d'éléments, tout comme font certaines autres macros qui comptent les arguments d'une __VA_ARGS__. C'est très bien trouvé, c'est ingénieux, mais je cherchais quelque chose de conçu pour faire ça dans le langage du préproc. Apparemment, ça n'existe pas.
PpHd (./87) :
avec le bon niveau de substitution

Alors voilà, j'en suis à essayer d'écrire des tucs qui marchent avec deux niveaux de substitution, et qui présentent un minimum d'intér^et pour mon besoin, c'est à dire autre chose que les exemples bateau de mon bouquin. On y va pas à pas, mais c'est sympa comme gymnastique d'esprit. grin

Merci encore pour tout happy
avatar<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !