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を使おうかなと考え中。ログ転送ができて面白い。実用で面白いと言うより、むしろデバッグ用で便利な可能性あり。
ただログ転送をするためにはまずネットワークを起動しないといけない。これをどうするか。
実際のところ、マルチスレッドにしてもロックなしでメッセージを投げっぱなしにできるところが楽できるかな、と思ったり。そんなに楽にならないかもしれないけど。逆に面倒かも。どうなんだ。