2012-06-05 11 views
5

sono un fan della stampa di debug quando si cerca di analizzare i problemi nel mio codice:Come si crea una macro (o un altro strumento) che utilizza il testo di determinate variabili in formato stringa?

cout << "foo:" << foo << "bar:" << bar << "baz:" << baz; 

che non scrivo il codice come questo, molto spesso, Sarebbe fantastico se potessi rendere più generica e più facile da digitare. Forse qualcosa di simile:

DEBUG_MACRO(foo, bar, baz); 

Anche se foo, bar, e baz volontà di nomi di variabili, non stringhe, è possibile utilizzare i loro nomi di variabili per creare le stringhe "foo:", "bar:", e "baz:"? Puoi scrivere una funzione o una macro che richiede un numero imprecisato di parametri?

+0

Usa buona vecchia stampa. – kol

+1

@kol printf non stringe i parametri, come il '' macro '# –

risposta

4

Se si dispone di C++ 11 si può fare qualcosa typesafe e abbastanza ordinata con i modelli variadic, ad esempio:

#include <string> 
#include <iostream> 

template <typename T> 
void debug(const T& v) { 
    std::cout << v << "\n"; 
} 

template <typename T, typename... Tail> 
void debug(const T& v, const Tail& ...args) { 
    std::cout << v << " "; 
    debug(args...); 
} 

#define NVP(x) #x":", (x) 

int main() { 
    int foo=0; 
    double bar=0.1; 
    std::string f="str"; 
    debug(NVP(foo),NVP(bar),NVP(f)); 
} 

La macro NVP qui è del tutto facoltativo e solo necessaria se si desidera stampare il nome a cui si riferisce anche nel codice.

Se davvero si vuole ignorare il bit NVP è possibile utilizzare la libreria Boost pre-processore (o rotolare il proprio) ad es .:

#include <string> 
#include <iostream> 
#include <boost/preprocessor/seq/for_each_i.hpp> 
#include <boost/preprocessor/punctuation/comma_if.hpp> 

template <typename T> 
void debug_impl(const T& v) { 
    std::cout << v << "\n"; 
} 

template <typename T, typename... Tail> 
void debug_impl(const T& v, const Tail& ...args) { 
    std::cout << v << " "; 
    debug_impl(args...); 
} 

#define NVP(x) #x":", (x) 
#define MEMBER(r, data, i, elem) BOOST_PP_COMMA_IF(i) NVP(elem) 

#define debug(members)        \ 
    debug_impl(           \ 
    BOOST_PP_SEQ_FOR_EACH_I(MEMBER,, members)   \ 
) 


int main() { 
    int foo=0; 
    double bar=0.1; 
    std::string f="str"; 
    debug((foo)(bar)(f)); 
} 

per il prezzo di una sintassi un po 'strana. Il mio esempio è basato su this answer. Ho provato ad usare macro variadic per risolvere questo problema direttamente come una soluzione "pura" in C++ 11, ma risulta che ricorrere in una lista è più complicato di quanto sperimenti con esso.

+0

Mi piace questa soluzione, ma non mi piace che 'NVP (foo)' sia quasi difficile da digitare come '" foo: "<< pippo'. Il prezzo che paghiamo per la sicurezza del tipo, immagino? –

+1

@CoryKlein - aggiornato con un esempio di come è possibile saltare il bit NVP. – Flexo

1
#define DEBUG_MACRO(name) std::cout << #name << " = " << name << std::endl; 

Esempio: http://ideone.com/agw4i

+1

Questo è un parametro singolo – kol

+0

Penso che questo approccio potrebbe funzionare in modo relativamente pulito includendo una sorta di file debug.h che fondamentalmente ha questa macro definita tante volte quante sono necessarie parametri. Ad esempio, disponi di un 'DEBUG_MACRO (nome)', 'DEBUG_MACRO (nome1, nome2)', e così via, assumendo che non avrai mai un numero infinito di variabili da digitare nella tua istruzione debug. –

0

Si può scrivere una funzione che prende un numero imprecisato di parametri?

Sì, ma di solito non si dovrebbe.

è possibile dichiarare una funzione che prende un numero imprecisato di parametri utilizzando le ellissi nella dichiarazione:

int foo(string foo, ...) 

ma ci sono grossi problemi con questo. Il problema più grande è che rompe la sicurezza del tipo. Considerare ciò che succede quando si utilizza un'altra funzione con una lista di parametri non specificato in modo errato :

int n = 42; 
char buf[256] = {}; 
sprintf(buf, "%s", n); 

Questo è un bug - ho specificato una stringa, ma superato un int. Il caso migliore è che questo verrà eseguito la prima volta che eseguo il debug del mio programma ed esplodo immediatamente. Molto più probabile tuttavia è che il codice non verrà mai eseguito fino alla prima volta che si verificherà una condizione eccezionale nella produzione, e quindi ci vorrà l'intero programma a metà giornata. I clienti chiamano e ti urlano contro, annulla unità, ecc ... OK, sono drammatico, ma il punto è tipo sicurezza è il tuo amico, anche quando hai un rapporto di amore-odio con esso.

+0

Sono d'accordo che nel caso generale, incoraggiare la sicurezza del tipo è la cosa buona da fare. Tuttavia, la funzionalità esiste per un motivo e sarebbe molto utile * se usata correttamente *, e questo specifico esempio è per le istruzioni di debug che vengono prontamente rimosse una volta risolto il problema. I messaggi di debug persistenti non sarebbero sicuramente dello stile sopra D: –

Problemi correlati