display.cc
本日はメッセージ出力を管理するクラスを再設計、書き直し。
Display display(); display.error() << "Message " << "output" << std::endl;
のように使って、エスケープシーケンスを出力してからメッセージを出力することにしていた。
以前のDisplayクラスは、Display::error()でエスケープシーケンスを出力した後、std::coutを返していた(あるいはNULLなstd::ostream&を返して、出力されないようにする)。で、今回ログもとりたくなったので、std::coutを返してしまうと困ってしまう。"output"以降のメッセージを受け取れないのでマズイ。
ということで、std:streambufをカスタマイズして、専用のostreamを作ることにする。
下手な日本語で書いても意味不明なので、コードで。
class UnionStreamBuffer : public std::streambuf { public: typedef boost::function<void (const char*)> StreamOutFunction; UnionStreamBuffer(); UnionStreamBuffer(StreamOutFunction out_func_1, StreamOutFunction out_func_2); public: void addStream(StreamOutFunction out_func); int sync(); private: char m_buffer[130]; std::vector<StreamOutFunction> m_stream_out; inline void setInitialPointers(void); };
template <message_priority L> class DisplayMapper { public: DisplayMapper(ConsoleControler& console, LogControler& log) : m_console(console), m_log(log), m_union_buffer( boost::bind(&ConsoleControler::write, m_console, _1, L), boost::bind(&LogControler::write, m_log, _1, L) ), m_formatter(&m_union_buffer) {} ~DisplayMapper(){} public: inline std::ostream& get(void) { return m_formatter; } private: ConsoleControler& m_console; LogControler& m_log; UnionStreamBuffer m_union_buffer; std::ostream m_formatter; };
だいぶ端折ったものの、だいたいこんな感じ。UnionStreamBufferで初期化したostreamにoperator<<で流していくと、自動的にコンソールとログの両方に出力される(ハズ。まだ中身は実装してない)
ConsoleControler console; LogControler log; DisplayMapper<ERROR> m_error(console, log); m_error.get() << "Message " << "output" << std::endl;
実際にはさらにDisplayIMPLクラスがDisplayMapperクラスのインスタンスを所有して、DisplayIMPLクラスを経由して各プライオリティ(ERROR, WARN, ...)のostreamにアクセスする。
さらにDisplayクラスがDisplayIMPLへのポインタを持つ。
class DisplayIMPL; class Display { public: Display(); ~Display(); public: std::ostream& error(void); std::ostream& warn(void); std::ostream& info(void); std::ostream& verbose(void); std::ostream& debug(void); private: DisplayIMPL *m_impl; };
DisplayIMPLを前方宣言して、DisplayIMPLの実際の宣言はincludeしない。そのか代わりDisplayIMPLの実体をメンバに持つことはできないので、ポインタを持つ。完全に『Effective C++』の「コンパイルするファイルの依存関係はできるだけ減らす」の受け売りでした。
Displayはほとんどのファイルからincludeされるものの、Displayの実装はすべてDisplayIMPLに書いているので、DisplayIMPLを変更してもすべてのファイルを再コンパイルする必要がない。
LogControlerはまだ実装していないけど、syslogを使おうかなと考え中。ログ転送ができて面白い。実用で面白いと言うより、むしろデバッグ用で便利な可能性あり。
ただログ転送をするためにはまずネットワークを起動しないといけない。これをどうするか。
実際のところ、マルチスレッドにしてもロックなしでメッセージを投げっぱなしにできるところが楽できるかな、と思ったり。そんなに楽にならないかもしれないけど。逆に面倒かも。どうなんだ。