ostreams только для отладки в C++?

Я реализовал ostream для вывода отладки, который отправляет, заканчивает тем, что отправил информацию об отладке в OutputDebugString. Типичное использование его похоже на это (где debug объект ostream):

debug << "some error\n";

Для сборок конечных версий, что является наименее болезненным и большая часть производительного способа не произвести эти операторы отладки?

12
задан pyrachi 3 April 2010 в 11:50
поделиться

5 ответов

Как насчет этого? Вам нужно будет проверить, что он фактически ничего не оптимизирует в выпуске:

#ifdef NDEBUG
    class DebugStream {};
    template <typename T>
    DebugStream &operator<<(DebugStream &s, T) { return s; }
#else
    typedef ostream DebugStream;
#endif

Вам нужно будет передать объект потока отладки как DebugStream &, а не как ostream &, поскольку в сборках выпуска он не является таковым. Это преимущество, поскольку, если ваш поток отладки не является ostream, это означает, что вы не подвергаетесь обычным штрафам во время выполнения, связанным с нулевым потоком, который поддерживает интерфейс ostream (виртуальные функции, которые фактически вызываются, но ничего не делают).

Предупреждение: я только что придумал это, обычно я бы сделал что-то похожее на ответ Нила - имел макрос, означающий «делать это только в отладочных сборках», чтобы в исходном коде было явно указано, что является отладочным кодом, а что нет. т. Некоторые вещи я не хочу абстрагироваться.

Макрос Нила также обладает тем свойством, что он совершенно определенно не оценивает свои аргументы в выпуске.Напротив, даже с моим встроенным шаблоном вы обнаружите, что иногда:

debug << someFunction() << "\n";

нельзя оптимизировать до нуля, потому что компилятор не обязательно знает, что someFunction () не имеет побочных эффектов. Конечно, если someFunction () действительно имеет побочные эффекты, тогда вы можете захотеть, чтобы он вызывался в сборках релизов, но это своеобразное сочетание протоколирования и функционального кода.

7
ответ дан 2 December 2019 в 05:15
поделиться
#ifdef RELEASE
  #define DBOUT( x )
#else
  #define DBOUT( x )  x
#endif

Просто используйте это в самих операторах ostream. Вы даже можете написать для него один оператор.

template<typename T> Debugstream::operator<<(T&& t) {
    DBOUT(ostream << std::forward<T>(t);) // where ostream is the internal stream object or type
}

Если ваш компилятор не может оптимизировать пустые функции в режиме выпуска, то пора получить новый компилятор.

Я, конечно, использовал ссылки rvalue и идеальную пересылку, и нет никакой гарантии, что у вас есть такой компилятор. Но вы, безусловно, можете просто использовать const ref, если ваш компилятор совместим только с C ++ 03.

2
ответ дан 2 December 2019 в 05:15
поделиться

Как уже было сказано, наиболее эффективный способ - использовать препроцессор. Обычно я избегаю препроцессора, но это единственное допустимое применение, которое я нашел для него, защищая заголовки.

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

В связи с этим мои макросы выглядят так:

#define TRACE_ERROR if (Debug::testLevel(Debug::Error)) DebugStream(Debug::Error)
#define TRACE_INFO  if (Debug::testLevel(Debug::Info))  DebugStream(Debug::Info)
#define TRACE_LOOP  if (Debug::testLevel(Debug::Loop))  DebugStream(Debug::Loop)
#define TRACE_FUNC  if (Debug::testLevel(Debug::Func))  DebugStream(Debug::Func)
#define TRACE_DEBUG if (Debug::testLevel(Debug::Debug)) DebugStream(Debug::Debug)

Хорошая вещь в использовании оператора if заключается в том, что трассировка, которая не выводится, не требует затрат, код трассировки вызывается только в том случае, если он будет напечатан.

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

#ifdef NDEBUG
    const bool Debug::DebugBuild = false;
#else
    const bool Debug::DebugBuild = true;
#endif

    #define TRACE_DEBUG if (Debug::DebugBuild && Debug::testLevel(Debug::Debug)) DebugStream(Debug::Debug)

При этом сохраняется синтаксис iostream, но теперь компилятор оптимизирует оператор if вне кода в сборках выпуска.

3
ответ дан 2 December 2019 в 05:15
поделиться

@iain: Не хватает места в поле для комментариев, поэтому разместите его здесь для ясности.

Использование операторов if - неплохая идея! Я знаю, что операторы if в макросах могут иметь некоторые подводные камни, поэтому мне нужно быть особенно осторожным при их построении и использовании. Например:

if (error) TRACE_DEBUG << "error";
else do_something_for_success();

... завершит выполнение do_something_for_success () , если произойдет ошибка и операторы трассировки на уровне отладки отключены, поскольку оператор else связывается с внутренним оператором if. Однако большинство стилей кодирования требуют использования фигурных скобок, которые решат проблему.

if (error) 
{
    TRACE_DEBUG << "error";
}
else
{
    do_something_for_success();
}

В этом фрагменте кода do_something_for_success () не выполняется ошибочно, если трассировка на уровне отладки отключена.

1
ответ дан 2 December 2019 в 05:15
поделиться

Самый распространенный (и, безусловно, самый эффективный) способ - удалить их с помощью препроцессора, используя что-то вроде этого (простейшая возможная реализация):

#ifdef RELEASE
  #define DBOUT( x )
#else
  #define DBOUT( x )  x
#endif

Затем вы можете сказать

DBOUT( debug << "some error\n" );

Изменить: Конечно, вы можете сделать DBOUT немного более сложным:

#define DBOUT( x ) \
   debug << x  << "\n"

, что позволяет использовать несколько более удобный синтаксис:

DBOUT( "Value is " << 42 );

Вторая альтернатива - определить DBOUT как поток. Это означает, что вы должны реализовать какой-то класс нулевого потока - см. Реализация no-op std :: ostream . Однако такой поток имеет служебные данные во время выполнения в сборке выпуска.

9
ответ дан 2 December 2019 в 05:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: