virtual streamsize xsputn(const char_type *_Ptr, streamsize _Count)
{
cout.write(_Ptr, _Count);
return std::filebuf::xsputn(_Ptr, _Count);
}
STL: лог-файл
Что-то я уже сделал, но решение мне не нравиться и не всегда работает.
Сделал так:
1. Создал производный класс от filebuf, в нем перегрузил пару виртуальных методов: xsputn, overflow. В конце вызываю реализацию filebuf, а перед этим вывожу на экран через cout:
Код:
2. Создал второй класс Log, производный от ostream, в конструкторе инициализирую его буффером Log_buf.
3. Создание лога выглядет так:
Код:
Log log("log.txt");
Как можно сделать это по-другому, более красиво? К тому же эта реализация криво работает для чисел, поэтому решил остановиться и спросить совета...
А стоит ли изобретать велосипед, посмотри в сторону boost, там этого добра хватает.
Если знаешь что-то конкретное из неё для решения проблемы, то почему бы не указать это?
С таким же успехом можно адресовать к google.
Boost.Log. Конкретней некуда :)
Цитата: DaemonDZK
Boost.Log. Конкретней некуда :)
Да нет, можно конкретнее: дай ссылку на конкретную библиотеку из Boost.
Если, кончно, найдешь такую... :D
Ты перед тем, как советы давать, сам то в boost заглядывал?
Цитата: vAC
Как можно реализовать лог-файл (класс, производный от ostream) с дублирующим выводом на экран?
Реализовать можно например так:
Потоковый буфер
Код:
template <typename CharType, class TraitsType = std::char_traits<CharType> >
struct basic_composed_streambuf : std::basic_streambuf<CharType, TraitsType>
{
typedef std::basic_streambuf<CharType, TraitsType> streambuf_type;
typedef std::set<streambuf_type*> streambuf_container;
typedef typename streambuf_container::iterator streambuf_iterator;
typedef std::streamsize streamsize;
streambuf_iterator insert(streambuf_type *pStreamBuf)
{
streambuf_iterator iter = streamBuffers.insert(pStreamBuf).first;
return iter;
}
void remove(streambuf_iterator iter)
{
streamBuffers.erase(iter);
}
protected:
virtual int_type overflow(int_type c = TraitsType::eof())
{
int_type result = TraitsType::not_eof(c);
for (streambuf_iterator current = streamBuffers.begin(), last = streamBuffers.end();
current != last;
++current)
{
int_type putResult = (*current)->sputc(c);
if (putResult == TraitsType::eof() || putResult == 0)
result = putResult;
}
return result;
}
virtual int sync()
{
int result = 0;
for (streambuf_iterator current = streamBuffers.begin(), last = streamBuffers.end();
current != last;
++current)
{
if (-1 == (*current)->pubsync())
result = -1;
}
return result;
}
virtual void imbue(const std::locale& _loc)
{
}
virtual streamsize xsputn(const CharType *ptr, streamsize count)
{
streamsize currentInserted = 0, inserted = 0;
for (streambuf_iterator current = streamBuffers.begin(), last = streamBuffers.end();
current != last;
++current)
{
currentInserted = (*current)->sputn(ptr, count);
inserted = max(inserted, currentInserted);
}
return inserted;
}
protected:
streambuf_container streamBuffers;
};
struct basic_composed_streambuf : std::basic_streambuf<CharType, TraitsType>
{
typedef std::basic_streambuf<CharType, TraitsType> streambuf_type;
typedef std::set<streambuf_type*> streambuf_container;
typedef typename streambuf_container::iterator streambuf_iterator;
typedef std::streamsize streamsize;
streambuf_iterator insert(streambuf_type *pStreamBuf)
{
streambuf_iterator iter = streamBuffers.insert(pStreamBuf).first;
return iter;
}
void remove(streambuf_iterator iter)
{
streamBuffers.erase(iter);
}
protected:
virtual int_type overflow(int_type c = TraitsType::eof())
{
int_type result = TraitsType::not_eof(c);
for (streambuf_iterator current = streamBuffers.begin(), last = streamBuffers.end();
current != last;
++current)
{
int_type putResult = (*current)->sputc(c);
if (putResult == TraitsType::eof() || putResult == 0)
result = putResult;
}
return result;
}
virtual int sync()
{
int result = 0;
for (streambuf_iterator current = streamBuffers.begin(), last = streamBuffers.end();
current != last;
++current)
{
if (-1 == (*current)->pubsync())
result = -1;
}
return result;
}
virtual void imbue(const std::locale& _loc)
{
}
virtual streamsize xsputn(const CharType *ptr, streamsize count)
{
streamsize currentInserted = 0, inserted = 0;
for (streambuf_iterator current = streamBuffers.begin(), last = streamBuffers.end();
current != last;
++current)
{
currentInserted = (*current)->sputn(ptr, count);
inserted = max(inserted, currentInserted);
}
return inserted;
}
protected:
streambuf_container streamBuffers;
};
Код:
template <typename CharType, class TraitsType = std::char_traits<CharType> >
struct basic_log : std::basic_ostream<CharType, TraitsType>
{
typedef std::basic_ostream<CharType, TraitsType> base_type;
typedef basic_composed_streambuf<CharType, TraitsType> composed_streambuf_type;
typedef std::basic_streambuf<CharType, TraitsType> streambuf_type;
typedef typename composed_streambuf_type::streambuf_iterator streambuf_id_type;
explicit basic_log(streambuf_type* pStreamBuf = NULL) : base_type(&composedStreamBuf)
{
if (pStreamBuf != NULL)
{
composedStreamBuf.insert(pStreamBuf);
}
}
streambuf_id_type addAnotherBuffer(streambuf_type *pBuffer)
{
return composedStreamBuf.insert(pBuffer);
}
void removeBuffer(streambuf_id_type id)
{
composedStreamBuf.remove(id);
}
protected:
composed_streambuf_type composedStreamBuf;
};
struct basic_log : std::basic_ostream<CharType, TraitsType>
{
typedef std::basic_ostream<CharType, TraitsType> base_type;
typedef basic_composed_streambuf<CharType, TraitsType> composed_streambuf_type;
typedef std::basic_streambuf<CharType, TraitsType> streambuf_type;
typedef typename composed_streambuf_type::streambuf_iterator streambuf_id_type;
explicit basic_log(streambuf_type* pStreamBuf = NULL) : base_type(&composedStreamBuf)
{
if (pStreamBuf != NULL)
{
composedStreamBuf.insert(pStreamBuf);
}
}
streambuf_id_type addAnotherBuffer(streambuf_type *pBuffer)
{
return composedStreamBuf.insert(pBuffer);
}
void removeBuffer(streambuf_id_type id)
{
composedStreamBuf.remove(id);
}
protected:
composed_streambuf_type composedStreamBuf;
};
Код:
#include <iostream>
#include <fstream>
#include <windows.h>
#include <streambuf>
#include <set>
...
typedef basic_log<char> logstream;
typedef basic_log<wchar_t> wlogstream;
int main(int argc, char* argv[])
{
std::filebuf fileBuf;
if (NULL == fileBuf.open("C:\\log.txt", std::ios_base::app | std::ios_base::out))
return -1;
logstream myLog(&fileBuf);
myLog << "GetTickCount = " << GetTickCount() << std::endl;
logstream::streambuf_id_type buffId = myLog.addAnotherBuffer(std::cout.rdbuf());
myLog << "Now also in the console and 5 = " << 5 << std::endl;
myLog.removeBuffer(buffId);
myLog << "This one is not gonna get to the console" << std::endl;
std::cin.get();
return 0;
}
#include <fstream>
#include <windows.h>
#include <streambuf>
#include <set>
...
typedef basic_log<char> logstream;
typedef basic_log<wchar_t> wlogstream;
int main(int argc, char* argv[])
{
std::filebuf fileBuf;
if (NULL == fileBuf.open("C:\\log.txt", std::ios_base::app | std::ios_base::out))
return -1;
logstream myLog(&fileBuf);
myLog << "GetTickCount = " << GetTickCount() << std::endl;
logstream::streambuf_id_type buffId = myLog.addAnotherBuffer(std::cout.rdbuf());
myLog << "Now also in the console and 5 = " << 5 << std::endl;
myLog.removeBuffer(buffId);
myLog << "This one is not gonna get to the console" << std::endl;
std::cin.get();
return 0;
}
P.S.: Для буфера, возможно, придётся реализовать ещё кое-какие виртуальные методы.
Цитата: k3Eahn
Реализовать можно например так:
Неплохо.
Вот только set я бы заменил на list, предоставив пользователю самому задуматься об уникальности.
И убрал бы из basic_composed_streambuf::overflow условие if (putResult == TraitsType::eof() || putResult == 0), т.к. вывод в один поток не должен влиять на вывод в другой. Другими словами: если у меня обломиться вывод в консоль, я все же хотел бы иметь соотв. строчку в файле.