Newsgroup: it.comp.lang.c
Titolo: Domanda su DEBUG printf

Ciao,
 
sto scrivendo un programma in C e vorrei sapere se la seguente cosa e'
fattibile.
 
 
 
1) Il programma e' composto da 10 file.
 
 
2) Ogni file puo' voler stampare una stringa di debug sullo standard
output, ad esempio:
 
 debug_printf (MODULO_1, "La temperatura e' %d", temperatura);
 debug_printf (MODULO_2, "Ho ricevuto il seguente carattere - %c",
ch);
 debug_printf (MODULO_3, "Ok, ho finito");
 
 
3) I vorrei fare le seguenti cose:
 
- eliminare tutte le chiamate "printf".
Cio' significa che tutte le "debug_printf" devono essere rimosse dal
preprocessore
 
- eliminare alcune chiamate printf e lasciarne attive altre
 
ciao,
grazie in anticipo
Enrico Migliore
 
 
Thanks in advance,
Enrico Migliore


In effetti è meno "googlabile" di quanto pensassi.
Prova qualcosa del tipo:
 
#ifdef DEBUG
#define debug_printf(...) \
     (printf("%s: ", __FILE__), printf(__VA_ARGS__))
#else
#define debug_printf(...) (void)0
#endif
 
Il segreto è ovviamente quel "__VA_ARGS__" (google), che spero sia 
supportato da qualunque commpilatore contemporaneo; quanto all'eliminare 
solo alcune chiamate... beh, lì puoi sbizzarrirti come ti pare, oppure 
compilare diversi moduli con/senza DEBUG -- questione di gusti.


Ci sono due soluzioni classiche con le macro: una è utilizzare la
macro ad argomenti varibili, feature C99, che non scrivo perché non mi
sa simpaticissima :-) l'altra è C89, che ti scrivo qui:
#ifdef DEBUG
#  define DEBUG_PRINT(_A) printf _A
#else
#  define DEBUG_PRINT(_A)
#endif
nel codice, metterai qualcosa tipo
DEBUG_PRINT(("errore %d su file %s!\n", errno, __FILE__));
e nota le doppie parentesi.
 
Per fare però *tutto* quello che chiedi meglio avere un modulo di
logger, che avrà un'interfaccia che classicamente è del tipo:
 
/* tipi di priorità */
typedef enum log_priority_e{ LOG_INFO, LOG_ERR, /*etc.*/}
t_log_priority_e;
 
/* attiva/disattiva in base alla priorità */
void log_set_priority(t_log_priority_e prio);
/* log del messaggio */
void log_msg(const char *fname, int line, t_log_priority_e prio, const
char *msg);
 
/* macro di utility */
#define LOG_INFO(_MSG) log_msg(__FILE__, __LINE__, LOG_INFO, (_MSG))
#define LOG_ERR(_MSG) log_msg(__FILE__, __LINE__, LOG_ERR, (_MSG))
 
o simili, condite come meglio credi..
 
Ciao!

Un modo semplice per abilitare/disabilitare messaggi di diagnostica a tempo 
di compoilazione e' questo:
 
1. Aggiungi queste due macro in qualche include file:
// #define PRINTF printf
// #define PRINTF if(0)printf
 
2. Implementa I messaggi diagnostici chiamando PRINTF invece di printf
 
3. Prima di compilare "scommenta" la prima definizione per abilitare I 
messaggi di diagnostica o la seconda se non li vuoi.
 
UDR
 
 
"Lorenzo Beretta"  wrote in message 
news:4ea6d8b2$0$1391$4faf...@reader1.news.tin.it...


Ciao,
 
grazie a tutti delle risposte.
 
Enrico Migliore


[...]
 
Non sono molto d'accordo con la scelta dell' "if" non accompagnato
da un "else". Il motivo potete facilmente immaginarlo ed è che
si presta a un errore di tipo "mismatched else". Ovvero, se la
macro si trova all'interno di un blocco "if" del tipo:
 
     if (i ==1)
         PRINTF("...");
     else
         ...;
 
disattivando il messaggio di diagnostica semplicemente con
 
     #define PRINTF if(0) printf
 
il blocco si espande in:
 
     if (i == 1)
         if(0) printf("...");
     else
         ...;
 
con l'associazione dell'istruzione "else" alla "if" più interna,
che è quella della macro! Blocco "else" che sarà tra l'altro sempre
eseguito quando i == 1 (esattamente il contrario di come dovrebbe
essere). Quindi la mia soluzione è:
 
     #define PRINTF if(1) {} else printf
 
Tuttavia, non mi piace molto utilizzare lo stesso nome, anche se in
maiuscolo. Personalmente sceglierei come bene ha fatto FtM, una
macro del tipo DEBUG_PRINTF. Ma non è finita qui. Generalmente
con la soluzione che vi ho proposto il compilatore non dovrebbe
generare codice per il blocco "if(1){}". Ma il compilatore, magari
non proprio moderno, potrebbe non essere abbastanza "intelligente"
da scartare completamente il codice per la "printf", dando luogo a un
inutile spreco di spazio, nonostante comunque non si avrebbe una
ricaduta nel run-time. In questo caso consiglio di fare il cast a
"void" sul valore di ritorno della funzione, quindi la soluzione
diventa:
 
     #ifdef DEBUG
     #define DEBUG_PRINTF printf
     #else
     #define DEBUG_PRINTF if(1) {} else (void)printf
     #endif
 
Ancora un appunto. Il blocco "if(1) {} else..." ha lo svantaggio
di essere uno "statement", mentre la chiamata a "printf" puo' anche
essere una "expression". Per esempio, in un ciclo "for":
 
     for(i = 0, DEBUG_PRINTF("sono nel ciclo for"); ...; ...)
 
tutto funziona in fase di debugging, ma nella rimozione della macro
questa viene sostituita con il blocco "if(1)..." che causa un
"syntax error". La soluzione è di usare l'operatore ternario ?: nel
seguente modo:
 
     #define DEBUG_PRINTF (1)?0:printf
 
(ho eliminato il "void" perchè il secondo e terzo argomento
dell'operatore ternario devono essere dello stesso tipo).
Questa soluzione ha minori "controindicazioni" rispetto alla
precedente. Ce ne sono tuttavia, come nella situazione:
 
     num = num + DEBUG_PRINTF("i = %d\n", i);
 
che quando DEBUG_PRINTF viene disabilitata diventa:
 
     num = num + (1)?0:printf("i = %d\n", i);
 
ed è un errore perchè in pratica il test diventa:
 
     num = (num +1)?0:printf("i = %d\n", i);
 
per le regole di precedenza degli operatori. Ma fortunatamente questo
tipo di errori basati sulla precedenza sono molto meno comuni.
 
-- 
Vincenzo Mercuri | 
backbox.org