Newsgroup: it.comp.lang.c
Titolo: Problemi con le macro

Ciao a tutti, ho un problema con un pezzo di codice:
ho definito ua macro per il debug in questo modo: 
TRACEF(a) { printf("file %s, linea %s"__FILE__,__LINE__); printf a; \     
\fflush(stdout) }
poi la richiamo per esempio cosě:
TRACE(("int x= %d\n",x));
a questo punto c'č il prolema:
in output trovo 
int x= tot  
file un_file, linea tot
Cioč PRIMA trovo quello che nella macro avevo definito DOPO e viceversa,
qualcuno sa spiegarmi perchč?                                           


I'm not prejudiced, I hate everyone equally.


perche' la stringa in printf() non ha il carattere di newline alla fine.
printf() stampa su stdout, che, essendo line-buffered, aspetta un newline prime
di essere svuotato, quindi la seconda printf ha effetto perche' contiene il
newline alla fine, mentre la prima viene stampata solo quando viene forzato il
flush del buffer.
Prova con:
/* prima soluzione: correggi la stringa */
TRACE(a) {printf("file %s, linea %s\n",__FILE__,__LINE__);printf a}
	/* in questo caso il fflush(stdout) non serve se (a) e` una stringa che
	 * termina con '\n'; sarebbe meglio aggiungerlo per sicurezza, ma e`
	 * ridondante */
/* seconda soluzione: fflush() anticipato */
TRACE(a) {printf("file %s, linea %s",__FILE__,__LINE__);fflush(stdout);printf a}
	/* idem come sopra, per la rischiesta del fflush(stdout) finale; in
	 * questo caso il newline alla fine della stringa della prima printf()
	 * non e` necessario perche' comunque il buffer viene forzato a
	 * svuotarsi subito dopo. E` codice meno performante del primo perche'
	 * chiama una funzione in piu`, ma potrebbe essere utile in rari casi */
/* terza soluzione: modifichi il comportamento di stdout */
TRACE(a) {printf("file %s, linea %s",__FILE__,__LINE__);printf a}
int main(argomenti_di_main){
	...
	setbuf(stdout,NULL);	/* all'inizio subito dopo la dichiarazione delle
				 * variabili */
	...
}
	/* in questo modo setti il comportamento di stdout a unbuffered
	 * (potresti usare anche setvbuf() o altre, questa e` piu` rapida),
	 * quindi non devi preoccuparti del suo svuotamento, poiche' nulla viene
	 * bufferizzato, viene tutto stampato in real-time. E` estremamente
	 * svantaggioso se hai una grossa quantita` di dati da stampare su
	 * stdout, perche' constringe il sistema ad una syscall per ogni singolo
	 * byte stampato a video (nei precedenti casi la syscall e` una per
	 * riga) */
/* soluzioni probabilmente ce ne sono altre mille,a te trovare la migliore ;) */
Adieu,
-- 
Lestat de Lioncourt                               |          Ciro Mattia Gonano
  JabberID: 
les...@jabber.students.cs.unibo.it
    |    Undergraduate Student in
  ICQ#: 52631406                                  |         Computer Science at
  URL: 
http://www.CS.UniBO.It/~gonano
             |       University of Bologna
            <powered by Linux Slackware 8.1 on Kernel 2.4.18-xfs>

flush del buffer.
Ho capito male o secondo te se scrivo
    printf("abc");
    printf("def\n");
la stringa "abc" viene scritta dopo "def" ??
Andrea

 
non ho detto questo, ho detto che "potrebbe" avverarsi, in quanto non svuoti il
buffer da abc.


 buffer da abc.
Sei sicuro? c'č il rischio dopo un printf("abc") che il buffer non venga
mai svuotato se succede qualcosa di straordinario (chiamata ad _exit(),
abort(), l'invio al processo di un segnale che provoca uscita immediata
dal processo... ma se in sequenza (tramite funzioni della libreria
standard impostate in modo che inviino output in line buffered) fai
stampare "abc" poi "def\n" lo stream viene svuotato da "abc" a "def\n",
non credo possa accadere che stampi prima "def\n" poi "abc" (anche
perchč dovrebbe aspettare un altro newline visto che in abc non c'č).
quindi sicuramente prima abc poi def\n.
credo di non sbagliarmi.
-- 
                    |   .-.
Gianluca Di Corato  |  : | :  I N  P E A C E  W E  T R U S T
   (aka sgrung)     |  '/|\'

 TRACE(("int x= %d\n",x));
Il codice così non dovrebbe neanche compilare bene perchè oltre agli
errori che ha messo in evidenza Lestat rimane che il preprocessore
traduce la macro in qualcosa del genere:
{printf("file %s, line %s...", "unfile", tot); printf ("int x=%d\n", x); fflush(stdout)};
(quello che ottieni da __LINE__ non è una stringa, ma un intero, devi
sostituire "line %s" con "line %d", rischi un segm-fault)
dopo fflush() dovrebbe esserci un ';' (punto e virgola) che invece
compare dopo la graffa.
Sarebbe meglio definire così:
#define TRACEF(a)	\
	printf("file %s, line %d...", __FILE__, __LINE__);	\
	printf a;	\
	fflush(stdout);
In questo modo una chiamata a TRACEF non richiederebbe l'uso del ';'
finale,
	TRACEF( ("int x=%d\n", x) )
IMHO è anche meglio esteticamente (?) :)
Ho inserito i tre puntini dopo "line %d" per dimostrare che il newline
non è necessario; per sicurezza comunque,
soprattutto in questo genere di situazioni, sarebbe meglio togliere il
buffering a stdout con setbuf o setvbuf (anche se sarebbe ancora meglio
inviare l'output da macro o funzioni di questo tipo sullo stderr, sempre
non bufferizzato e IMO è più logico inviare a questo stream l'output di
funzioni di tracing piuttosto che allo stdout).
Funzione, e soprattutto non stampa prima il secondo printf, buon divertimento!


 
ok, potrebbe anche essere, in effetti rileggendo il mio psot di ieri sera, era
quasi tutto corretto, pero` partiva dalla premessa (sbagliata) che questa fosse
la causa dello svuotamento asincrono del buffer. Ovverosia, ho immaginato (non
chiedermi perche', lo sto gia` chiedendo io alla mia mente malata :P ) che le
due printf() allo stesso tempo avessero buffer differenti ma concatenati. Un po'
difficile da spiegare, ma sbagliato, quindi non mi ci metto neppure :)
Rimane il fatto che altrimenti non riesco a trovare dov'e` il problema... :/


 IMHO è anche meglio esteticamente (?) :)
Io definirei TRACEF cosi:
#define TRACEF(a) \
        do{ \
                printf("file %s, line %d...", __FILE__, __LINE__);      \
                printf a;       \
                fflush(stdout); \
        }while(0) 
SENZA il ; finale.
In questa maniera non si hanno sorprese strane in codice del tipo
if( a )
        TRACEF(("int x=%d\n", x)) ;
else
        .....
Con la tua macro bisognerebbe scriverlo
if( a ){
        TRACEF(("int x=%d\n", x)) 
}else
        .....
Un po' OT, ma io con il GCC sono solito sfruttare una sua estensione:
#define TRACEF(a...) \
        do{ \
                printf("file %s, function %s, line %d...", \
                        __FILE__, __FUNCTION__, __LINE__);      \
                printf( a );    \
                fflush(stdout); \
        }while(0) 
Per cui vado a scrivere
if( a )
        TRACEF("int x=%d\n", x);
else
        .....
Che mi sembra più naturale ancora.... Purtroppo non è standard C

 abort(), l'invio al processo di un segnale che provoca uscita immediata
Mi correggo, abort() svuota tutti gli stream aperti.
