Перенаправить вывод отладки в нулевой поток вместо std:: cerr

89
10

Библиотека программного обеспечения, с которой я работаю, записывает много отладочного вывода в std::cerr, но перенаправляет этот вывод на нулевой поток, если я говорю, что он тихий. Это упрощенный main.cpp, который показывает, как код пытается это сделать:


#include <iostream>
#include <fstream>
#include <cassert>

// The stream that debug output is sent to. By default
// this points to std::cerr.
std::ostream* debugStream(&std::cerr);

// Throughout the library codebase this function is called
// to get the stream that debug output should be sent to.
std::ostream& DebugStream()
{
return *debugStream;
}

// Null stream. This file stream will never be opened and acts
// as a null stream for DebugStream().
std::ofstream nullStream;

// Redirects debug output to the null stream
void BeQuiet()
{
debugStream = &nullStream;
}

int main(int argc, char** argv)
{
DebugStream() << "foo" << std::endl;
BeQuiet();
DebugStream() << "bar" << std::endl;
assert(debugStream->good());

return 0;
}


Когда вы запустите эту программу, вы заметите, что строка "bar" правильно отправлена ​​в нулевой поток. Однако я заметил, что утверждение терпит неудачу. Это что-то, о чем я должен беспокоиться? Или это просто немного уродливая деталь подхода, выбранного разработчиками библиотеки?


Если вам так хочется, предложения по лучшим альтернативам приветствуются. Некоторые ограничения:


    Библиотека является кросс-платформенной, поэтому я думаю, что открытие /dev/null не является допустимым решением, так как оно не будет работать в Windows
    Библиотека использует стандартный С++, поэтому любые альтернативные решения не должны использовать специфичные для компилятора вещи

спросил(а) 2021-01-25T09:33:13+03:00 4 месяца, 4 недели назад
1
Решение
100

Нет необходимости беспокоиться о том, что поток не является good()! Поскольку операторы вывода действительно ничего не делают с потоком в режиме сбоя, различные регистрируемые объекты не форматируются, то есть код работает быстрее по сравнению с альтернативными подходами.


Обратите внимание, что вам действительно не нужен второй поток для отключения вывода:


    Предполагая, что все операторы вывода корректны, вы можете просто установить std::ios_base::failbit:


    debugStream().setstate(std::ios_base::failbit);

    Если есть ошибочный вывод, который записывается в поток, даже если он не является good(), вы можете просто установить его буфер потока в значение null:


    debugStream() rdbuf (nullptr);.


Если вы действительно хотите, чтобы ваш поток оставался в состоянии good(), вы должны установить буфер потока, который просто потребляет символы. Обратите внимание, однако, что вы хотите предоставить этот буфер потока буфер, имеющий overflow(), вызываемый для каждого char, достаточно обходителен:

struct nullbuf
: std::streambuf {
char buf[256];
int overflow(int c) {
this->setp(this->buf, this->buf + 256);
return std::char_traits<char>::not_eof(c);
}
};
...
nullbuf sbuf;
debugStream().rdbuf(&sbuf);
...
debugStream().rdbuf(0);

Необходимо reset буфер потока потока, потому что деструктор std::ostream будет очищать буфер stresm (т.е. вызывает pubsync()). Выполнение этого в поврежденном буфере потока не будет работать.


Лично я бы пошел с настройкой std::ios_base::failbit.

ответил(а) 2021-01-25T09:33:13+03:00 4 месяца, 4 недели назад
89

В этом ответе вы найдете общий помощник для перенаправления любого потока на любой другой поток:


class stream_redirector {
public:
stream_redirector(std::ios& stream, std::streambuf* newBuf) :
savedBuf_(stream.rdbuf()), stream_(stream)
{
stream_.rdbuf(newBuf);
}

~stream_redirector() {
stream_.rdbuf(savedBuf_);
}

private:
std::streambuf* savedBuf_;
std::ios& stream_;
};


и теперь вам нужен только поток no-op из этого ответа, который отбрасывает что-либо:


template <class cT, class traits = std::char_traits<cT> >
class basic_nullbuf: public std::basic_streambuf<cT, traits> {
typename traits::int_type overflow(typename traits::int_type c)
{
return traits::not_eof(c); // indicate success
}
};

template <class cT, class traits = std::char_traits<cT> >
class basic_onullstream: public std::basic_ostream<cT, traits> {
public:
basic_onullstream():
std::basic_ios<cT, traits>(&m_sbuf),
std::basic_ostream<cT, traits>(&m_sbuf)
{
// note: the original code is missing the required this->
this->init(&m_sbuf);
}

private:
basic_nullbuf<cT, traits> m_sbuf;
};

typedef basic_onullstream<char> onullstream;
typedef basic_onullstream<wchar_t> wonullstream;

и вам будет хорошо:


int main(int argc, char** argv)
{
std::cerr << "foo" << std::endl;
{
onullstream nos;
stream_redirector sr( std::cerr, nos.rdbuf() );
std::cerr << "bar" << std::endl;
}
std::cerr << "baz" << std::endl;
}

и, наконец, здесь живой пример, если вы хотите играть с ним. Этот метод имеет дополнительное преимущество, которое вы (и ваши коллеги) можете использовать std::cerr в своем коде, и вы можете отключить его и снова включить:)

ответил(а) 2021-01-25T09:33:13+03:00 4 месяца, 4 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема