2009-01-18 9 views
6

estoy usando una aplicación en C++ que utiliza una función especial dprintf para imprimir la información, este es un ejemplo:imprimir informacion en "modo de prueba" pero no en "la ejecución normal"

dprintf(verbose, "The value is: %d", i); 

Lo que está haciendo es cuando defino verbose para propósitos de prueba, imprimo la información y cuando estoy trabajando en ejecución normal no la defino y no veo información inútil en la pantalla. Mi pregunta es ¿cómo puedo hacer esa función o implementar la misma idea ?.

Respuesta

14

trato de evitar el uso de las funciones de c-estilo var-arg por dos razones principales:

  • No son de tipo seguro, no puede utilizar el operador < <
  • No reconocen cuando se proporcionaron muy pocos o muchos argumentos

He hecho una forma que funciona usando boost::fusion, que se da argumentos de forma segura. Se repite sobre esos argumentos, imprimiéndolos cuando se encuentra un %. Si se dieron demasiados o demasiados argumentos, se lanza una excepción.

Todavía hay un problema: las macros variables no son aún estándar en C++. Entonces, he hecho dos versiones. Uno que funciona con C++ actual. Tiene que invocarlo usando

dprintf("name: %, value: %\n", ("foo", 42)); 

Luego. La otra versión, el uso de macros variadic, se puede utilizar mediante la definición de un símbolo de preprocesador, que le permite escribir

dprintf("name: %, value: %\n", "foo", 42); 

Aquí está el código. El boost.fusion proporciona más detalles sobre esto,

#include <boost/fusion/include/sequence.hpp> 
#include <boost/fusion/include/make_vector.hpp> 
#include <boost/fusion/include/next.hpp> 
#include <stdexcept> 
#include <iostream> 

template<typename IterS, typename IterSeqE> 
void print_vec(IterS b, IterS e, IterSeqE, IterSeqE) { 
    while(b != e) { 
     if(*b == '%') { 
      if(++b != e && *b == '%') { 
       std::cout << '%'; 
      } else { 
       throw std::invalid_argument("too many '%'"); 
      } 
     } else { 
      std::cout << *b; 
     } 
     ++b; 
    } 
} 

template<typename IterS, typename IterSeqB, typename IterSeqE> 
void print_vec(IterS b, IterS e, IterSeqB seqb, IterSeqE seqe) { 
    while(b != e) { 
     if(*b == '%') { 
      if(++b != e && *b == '%') { 
       std::cout << '%'; 
      } else { 
       std::cout << *seqb; 
       return print_vec(b, e, next(seqb), seqe); 
      } 
     } else { 
      std::cout << *b; 
     } 
     ++b; 
    } 
    throw std::invalid_argument("too few '%'"); 
} 

template<typename Seq> 
void print_vec(std::string const& msg, Seq const& seq) { 
    print_vec(msg.begin(), msg.end(), begin(seq), end(seq)); 
} 

#ifdef USE_VARIADIC_MACRO 
# ifdef DEBUG 
# define dprintf(format, ...) \ 
     print_vec(format, boost::fusion::make_vector(__VA_ARGS__)) 
# else 
# define dprintf(format, ...) 
# endif 
#else 
# ifdef DEBUG 
# define dprintf(format, args) \ 
     print_vec(format, boost::fusion::make_vector args) 
# else 
# define dprintf(format, args) 
# endif 
#endif 

// test, using the compatible version. 
int main() { 
    dprintf("hello %, i'm % years old\n", ("litb", 22)); 
} 
+1

No puedo hablar de otros compiladores, pero para GCC, -What y __attribute __ ((format)) son sus amigos. Eso permite API de estilo printf con todas las bondades de verificación de tipo del operador <<. – Tom

+1

sí, soy consciente de esos atributos. pero no te permitirá pasar tus propios tipos. por ejemplo, puede usar una lista. y quiere sobrecargar el operador << para su lista. con printf, debe escribir el mismo ciclo nuevamente y está limitado a ciertos tipos de elementos que printf entiende. –

+0

operador de sobrecarga << permite poner el código de impresión en el lado del tipo que desea imprimir, que prefiero poner el código de impresión al costado de mi código de depuración.por lo tanto, cada vez que tenga algo imprimible, su solución dprintf de tipo seguro ya lo entiende. –

9
#ifdef DEBUG 
#define dprintf(format, ...) real_dprintf(format, __VA_ARGS__) 
#else 
#define dprintf 
#endif 

Aquí real_dprintf() es la función de "real" que se invoca, y dprintf() es sólo una macro envolver la llamada.

+0

que iba a publicar la misma respuesta, por lo que guardarlo y Upvote la suya. Esta es la única situación en la que uso macros de preprocesador en código C++. –

4

La solución del preprocesador funcionará, pero puede ser molesto tener que reconstruir para cambiar de una a la otra. A menudo tomaré la decisión en tiempo de ejecución. La primera vez que declarar:

static void do_nothing(const char *fmt, ...) { (void)fmt; } 
extern void real_dprintf(const char *fmt, ...); 
void (*dprintf)(const char *fmt, ...) = do_nothing; 

Luego, en el código de inicialización tengo

if (getenv("APPLICATION") && strstr(getenv("APPLICATION"), "dprintf")) 
    dprintf = real_dprintf; 

De esta manera puedo cambiar rápidamente modos cambiando el valor de una variable de entorno.

Cuestiones relacionadas