2009-01-18 1 views
6

Я использую приложение в C++, который использует специальную функцию dprintf для печати информации, это пример:информации для печати в «тестовом режиме», но не в «нормальном исполнении»

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

Что делает это когда я определяю подробные для целей тестирования, я печатаю информацию, и когда я работаю в обычном режиме, я не определяю ее, и я не вижу бесполезную информацию на экране. Мой вопрос: как я могу выполнить эту функцию или реализовать ту же идею ?.

ответ

14

Я стараюсь избегать использования вар-Arg функции C-стиля по двум основным причинам:

  • Они не типобезопасным, не может использовать оператор < <
  • Они не признают, когда слишком мало или много аргументов были предоставлены

Я сделал способ, который работает с использованием boost::fusion, который предоставляет аргументы безопасным способом. Он повторяет эти аргументы, распечатывая их, когда встречается %. Если задано слишком мало или слишком много аргументов, генерируется исключение.

Есть еще одна проблема: макросы Variadic еще не являются стандартными в C++. Итак, я сделал две версии. Один, который работает с текущим C++. Вы должны использовать его, используя

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

Затем. Другой вариант, используя VARIADIC макросы, можно использовать, определив символ препроцессора, который позволяет писать

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

Вот код. boost.fusion содержит более подробную информацию для этого:

#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

Я не могу говорить для других компиляторов, но для GCC, -Wformat и __attribute __ ((format)) являются вашими друзьями. Это позволяет использовать API-интерфейсы типа printf со всей проверкой корректности оператора <<. – Tom

+1

Да, я знаю об этих атрибутах. но это не позволит вам передавать собственные типы. например, вы можете использовать список. и хотите перегрузить оператор << для вашего списка. с printf, вы должны снова написать один и тот же цикл и ограничены определенными типами элементов, которые понимает printf. –

+0

оператор перегрузки << позволяет печатать код на стороне типа, который вы хотите распечатать, который я предпочитаю помещать код печати на стороне моего кода отладки.поэтому, всякий раз, когда у вас есть что-то пригодное для печати, ваше безопасное для печати решение dprintf уже понимает его. –

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

Здесь real_dprintf() является «реальной» функция, которая вызывается, и dprintf() просто макро оберточной вызов.

+0

Я собирался отправить один и тот же ответ, так что я его сохранить и upvote ваши. Это единственная ситуация, когда я использую макросы препроцессора в коде C++. –

4

Решение препроцессора будет работать, но может быть неприятно перестраиваться, чтобы переходить от одного к другому. Я часто буду принимать решение во время выполнения. Я первый заявляю:

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

Затем в коде инициализации меня

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

Таким образом, я могу быстро изменить режим, изменив значение переменной окружения.

 Смежные вопросы

  • Нет связанных вопросов^_^