Печатать информацию в тестовом режиме, но не в обычном исполнении

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

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

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


person Eduardo    schedule 18.01.2009    source источник


Ответы (3)


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

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

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

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

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

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

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));
}
person Johannes Schaub - litb    schedule 19.01.2009
comment
Я не могу говорить за другие компиляторы, но для GCC ваши друзья -Wformat и __attribute__((format)) . Это позволяет использовать API-интерфейсы в стиле printf со всеми преимуществами проверки типов оператора‹‹. - person Tom; 19.01.2009
comment
Да, я знаю об этих атрибутах. но это не позволит вам передавать собственные типы. например, вы можете использовать список. и хотите перегрузить оператор‹‹ для вашего списка. с printf вам нужно снова написать один и тот же цикл, и вы ограничены определенными типами элементов, которые понимает printf. - person Johannes Schaub - litb; 19.01.2009
comment
оператор перегрузки‹‹ позволяет поместить код печати на стороне типа, который вы хотите напечатать, что я предпочитаю помещать код печати на стороне моего кода отладки. поэтому всякий раз, когда у вас есть что-то для печати, ваше решение dprintf с типобезопасностью уже понимает это. - person Johannes Schaub - litb; 19.01.2009
comment
это то самое улучшение, которое C++ сделал по сравнению с C, что вы можете разделить проблемы и совместно использовать код между библиотеками. если тип доступен для печати, он перегружает оператор ‹‹, и каждый может его использовать. мне это очень нравится. - person Johannes Schaub - litb; 19.01.2009
comment
Я согласен с тем, что любой объект можно распечатать, но я считаю, что некоторые объекты необходимо сериализовать в 2-3 разных режимах. Двоичная сериализация для хранения, базовой печати и дампа отладки. Механизм ostream‹‹ предполагает, что объект имеет только один механизм сериализации. - person Tom; 19.01.2009
comment
... так что я заканчиваю тем, что 2-3 реализую отдельные подпрограммы (принимая ostream& в качестве параметра), а не использую ostream‹‹. - person Tom; 19.01.2009

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

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

person Joao da Silva    schedule 18.01.2009
comment
Я собирался опубликовать тот же ответ, поэтому сохраню его и проголосую за ваш. Это единственная ситуация, в которой я использую макросы препроцессора в коде C++. - person Daniel Daranas; 19.01.2009

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

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;

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

person Norman Ramsey    schedule 18.01.2009