Реализация no-op std::ostream
Я смотрю на создание класса ведения журнала, в котором есть такие элементы, как Info, Error и т. Д., Которые можно настраивать для вывода на консоль, в файл или в никуда.
Для эффективности я хотел бы избежать накладных расходов на форматирование сообщений, которые будут выбрасываться (т.е. информационные сообщения, когда они не работают в подробном режиме). Если я реализую собственный std::streambuf, который выводит в никуда, я представляю, что слой std:: ostream все равно будет выполнять все форматирование. Может кто-нибудь предложить способ получить действительно "нулевой" std::ostream, который вообще не будет выполнять какую-либо работу с параметрами, передаваемыми ему <<
?
Благодарю.
5 ответов
Чтобы предотвратить operator<<()
Вызовы от выполнения форматирования, вы должны знать тип потока во время компиляции. Это можно сделать с помощью макросов или шаблонов.
Мой шаблон решения следует.
class NullStream {
public:
void setFile() { /* no-op */ }
template<typename TPrintable>
NullStream& operator<<(TPrintable const&)
{ /* no-op */ }
}
template<class TErrorStream> // add TInfoStream etc
class Logger {
public:
TErrorStream& errorStream() {
return m_errorStream;
}
private:
TErrorStream m_errorStream;
};
//usage
int main() {
Logger<std::ofstream> normal_logger; // does real output
normal_logger.errorStream().open("out.txt");
normal_logger.errorStream() << "My age is " << 19;
Logger<NullStream> null_logger; // does zero output with zero overhead
null_logger.errorStream().open("out.txt"); // no-op
null_logger.errorStream() << "My age is " << 19; // no-op
}
Поскольку вы должны сделать это во время компиляции, это, конечно, довольно негибко.
Например, вы не можете определить уровень ведения журнала во время выполнения из файла конфигурации.
Свифт Google придумала этот пример, который может быть полезен. Я не даю никаких гарантий, кроме того, что он компилируется и запускается:-)
#include <streambuf>
#include <ostream>
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)
{
init(&m_sbuf);
}
private:
basic_nullbuf<cT, traits> m_sbuf;
};
typedef basic_onullstream<char> onullstream;
typedef basic_onullstream<wchar_t> wonullstream;
int main() {
onullstream os;
os << 666;
}
Всем, спасибо, что поделились кодом, я просто делаю тест, тогда метод Нейла все равно будет выполнять форматирование строки, например:
#include <streambuf>
#include <ostream>
#include <iostream>
using namespace std;
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)
{
init(&m_sbuf);
}
private:
basic_nullbuf<cT, traits> m_sbuf;
};
typedef basic_onullstream<char> onullstream;
typedef basic_onullstream<wchar_t> wonullstream;
class MyClass
{
int a;
friend ostream& operator<< (ostream&, MyClass const&);
};
ostream& operator<<(ostream& out,MyClass const& b)
{
std::cout<<"call format function!!";
out << b.a;
return out;
}
int main() {
onullstream os;
MyClass obj;
os<<obj;
}
Запустив эту программу, вы обнаружите, что будет вызываться "ostream& operator<< (ostream & out, MyClass const & b)". Таким образом, выполнение формата в объекте все равно будет вызываться. Таким образом, мы все еще не можем избежать накладных расходов на форматирование сообщений.
Возможно, вам понадобится больше, чем просто форматирование текста и фильтрация сообщений. Как насчет многопоточности?
Я бы реализовал фильтрацию и многопоточную синхронизацию как ответственность отдельного класса.
Тем не менее, ведение журнала является не такой простой проблемой, и я бы попытался использовать существующие решения для ведения журнала, вместо разработки нового.
Почему бы не использовать существующие решения для ведения журналов, используемые миллионами пользователей? log4j, log4net, log4cxx.., чтобы назвать только несколько..