【行単位で排他】std::ostreamのお手軽カスタマイズ【時刻付き】(2)
ソース
※↓↓↓「仮想メディア」なる語はGMA0BNが勝手に作ったもの
これわ実質的にバッファなんだけども、std::streambufが管理するバッファと紛らわしいので区別を付ける意味で、
<SafeStreamBuffer.h>
#pragma once #define LOG_DATE_FORMAT 2 class SafeStreamBuffer : public std::streambuf { private: typedef char _Elem; typedef std::char_traits<char> _Traits; typedef _Traits::int_type int_type; private: _Elem* m_aElemOut; // 仮想メディア実体(出力方向) int bufSz; int wrIdx; int rdIdx; /*= 出力メインのクラスなので、 * 入力方向の仮想メディアは持たない。 */ std::istream& m_ist; std::ostream& m_ost; CCritSec& m_syncRoot; public: /// 新しいインスタンスを初期化します。 SafeStreamBuffer(std::istream& ist, std::ostream& ost, CCritSec& syncRoot); private: /// (禁止) SafeStreamBuffer(const SafeStreamBuffer&); public: /// インスタンスを破棄します。 virtual ~SafeStreamBuffer(); /// ストリームに1文字書き込まれたとき呼ばれます。 virtual int_type overflow(int_type ch = _Traits::eof()); /// ストリームから1文字読み出されるとき呼ばれます。 virtual int_type underflow(); /// 仮想メディアから1文字読み出します。 virtual int_type uflow(); /// 読み出した文字を仮想メディアに戻します。 virtual int_type pbackfail(int_type ch = _Traits::eof()); /// 仮想メディアと物理メディアを同期します。 virtual int_type sync(); private: /// 現在の日時を出力します。(秒まで) static std::ostream& PutDateClassic(std::ostream& stream); /// 現在の日時を出力します。(msまで) static std::ostream& PutDateFine(std::ostream& stream); };
<SafeStreamBuffer.cpp>
#include "stdafx.h" #include "SafeStreamBuffer.h" using namespace std; const int bufSzStd = 512; const int bufSzDelta = 512; /// 新しいインスタンスを初期化します。 SafeStreamBuffer::SafeStreamBuffer(std::istream& ist, std::ostream& ost, CCritSec& syncRoot) : wrIdx(0), rdIdx(0), bufSz(bufSzStd), m_ist(ist), m_ost(ost), m_syncRoot(syncRoot) { setbuf(0, 0); // std::streambuf内でバッファリングしない m_aElemOut = (_Elem*)malloc((bufSz + 1) * sizeof(_Elem)); } /// インスタンスを破棄します。 SafeStreamBuffer::~SafeStreamBuffer() { free(m_aElemOut); } /// (std::streambuf内でバッファリングしないので) /// ストリームに1文字書き込まれたとき呼ばれます。 SafeStreamBuffer::int_type SafeStreamBuffer::overflow(int_type ch) { const int_type eof = _Traits::eof(); if (ch == eof) return eof; if (wrIdx >= bufSz) { int n = bufSz + bufSzDelta; _Elem* p = (_Elem*)realloc(m_aElemOut, (n + 1) * sizeof(_Elem)); if (p == 0) return EOF; m_aElemOut = p; bufSz = n; } m_aElemOut[wrIdx++] = ch; return 0; } /// (std::streambuf内でバッファリングしないので) /// ストリームから1文字読み出されるとき呼ばれます。 SafeStreamBuffer::int_type SafeStreamBuffer::underflow() { return pbackfail(uflow()); } /// 仮想メディアから1文字読み出します。 SafeStreamBuffer::int_type SafeStreamBuffer::uflow() { const int_type eof = _Traits::eof(); if (rdIdx >= wrIdx) return eof; // 読むべきデータ無し return m_aElemOut[rdIdx++]; /*= 出力メインのクラスなので、 * 書き込んだ内容のうち、出力前のものが読めるだけ、 * という適当実装。 */ } /// 読み出した文字を仮想メディアに戻します。 SafeStreamBuffer::int_type SafeStreamBuffer::pbackfail(int_type ch) { const int_type eof = _Traits::eof(); if (ch == eof) return eof; if (rdIdx <= 0) return eof; --rdIdx; if (m_aElemOut[rdIdx] != ch) return eof; return 0; } /// 現在の日時を出力します。(秒まで) std::ostream& SafeStreamBuffer::PutDateClassic(std::ostream& ost) { time_t timer; time(&timer); struct tm t_st; errno_t err = localtime_s(&t_st, &timer); if (err == 0) { int year = t_st.tm_year + 1900; int month = t_st.tm_mon + 1; int day = t_st.tm_mday; int hour = t_st.tm_hour; int min = t_st.tm_min; int sec = t_st.tm_mon; // 基数とfillCharを変更 ios::fmtflags sv_f = ost.setf(ios::dec, ios::basefield); ost.unsetf(ios::showpos); _Elem sv_c = ost.fill('0'); // 日時出力 ost << "[" << setw(4) << year << "-" << setw(2) << day << "-" << setw(2) << day << "T" << setw(2) << hour << ":" << setw(2) << min << ":" << setw(2) << sec << "] "; // 元に戻す ost.fill(sv_c); ost.setf(sv_f); } return ost; } /// 現在の日時を出力します。(msまで) std::ostream& SafeStreamBuffer::PutDateFine(std::ostream& ost) { SYSTEMTIME t_st; GetLocalTime(&t_st); int year = t_st.wYear; int month = t_st.wMonth; int day = t_st.wDay; int hour = t_st.wHour; int min = t_st.wMinute; int sec = t_st.wSecond; int msec = t_st.wMilliseconds; // 基数とfillCharを変更 ios::fmtflags sv_f = ost.setf(ios::dec, ios::basefield); ost.unsetf(ios::showpos); _Elem sv_c = ost.fill('0'); // 日時出力 ost << "[" << setw(4) << year << "-" << setw(2) << month << "-" << setw(2) << day << "T" << setw(2) << hour << ":" << setw(2) << min << ":" << setw(2) << sec << "." << setw(4) << msec << "] "; // 元に戻す ost.fill(sv_c); ost.setf(sv_f); return ost; } /// 仮想メディアと物理メディアを同期します。 SafeStreamBuffer::int_type SafeStreamBuffer::sync() { CAutoLock lock(&m_syncRoot); /*= 以降ブロックを抜けるまでがクリティカルセクションになる。 */ // 日時を記録 #if LOG_DATE_FORMAT == 1 PutDateClassic(m_ost); #elif LOG_DATE_FORMAT == 2 PutDateFine(m_ost); #endif // 仮想メディア内容を出力 m_aElemOut[wrIdx] = 0; // 終端文字('\0')付加 m_ost << m_aElemOut << flush; // 仮想メディアを空にする rdIdx = wrIdx = 0; return (m_ost.fail()) ? _Traits::eof() : 0; /*= 出力メインのクラスなので * 入力方向の同期(物理メディア→仮想メディア)は省略。 */ }
今回は目的と時間との兼ね合いによりchar文字列にしか対応していないが、もちろんこれはbasic_ostreamやbasic_istreamといったテンプレートを使えばワイド文字対応も可能*1