3

そうです、これを正しく定式化する方法さえわかりません。少し複雑な質問だと思います。誰かがここで私を助けてくれると確信しています。

これは私がやりたいことです:

  1. このように、ものを送ることができる単一のクラスを用意します。

    icl << "Blah blah blah" << std::endl;
    
  2. std::basic_ostream を継承する .attach() クラスをそれにできるようにしたいです。

  3. これらのクラスは、独自の方法で出力をフォーマットできます。1 つはタイムスタンプを追加してログに書き込み、もう 1 つはそれをコンソールに書き込み、もう 1 つはゲーム内に表示します。

誰かが私を正しい方向に導きたいと思っていますか? これが私がかなり持っているアイデアです。

#include <vector>

class OutputMan {
    std::vector<std::basic_ostream&> m_Streams;

public:
    void attach(std::basic_ostream& os) {
        m_Streams.push_back(os);
    }
}

質問 #1:送信するために何を継承してオーバーライドする必要がありますか?

icl << "Blah!" << std::endl;

m_Streams のすべてのストリームに?

質問 2: std::basic_ostream を継承し、出力を変更するクラスを作成するにはどうすればよいですか。たとえば、出力の先頭にタイムスタンプを追加します。また、このクラスをファイルに出力したいと考えています。

4

2 に答える 2

5

少し違うことをすると思います。これはおそらく必要以上に手の込んだものでした。C++11 の新しい機能を有効に活用しようとして少し夢中になってしまったのではないでしょうか。とにかく、コードを続けます:

#include <streambuf>
#include <fstream>
#include <vector>
#include <iostream>
#include <initializer_list>

namespace multi { 
class buf: public std::streambuf {
    std::vector<std::streambuf *> buffers;
public:
    typedef std::char_traits<char> traits_type;
    typedef traits_type::int_type  int_type;

    buf(std::vector<std::ofstream> &buf) {
        for (std::ofstream &os : buf)
            buffers.push_back(os.rdbuf());
    }

    void attach(std::streambuf *b) { buffers.push_back(b); }

    int_type overflow(int_type c) {
        bool eof = false;
        for (std::streambuf *buf : buffers) 
            eof |= (buf -> sputc(c) == traits_type::eof());
        return eof ? traits_type::eof() : c;
    }   
};

class stream : public std::ostream { 
    std::vector<std::ofstream> streams;
    buf outputs;
public:   
    stream(std::initializer_list<std::string> names)
        : streams(names.begin(), names.end()), 
          outputs(streams), 
          std::ostream(&outputs) 
    { }
    void attach(std::ostream &b) {
        outputs.attach(b.rdbuf());
    }
};
}

int main() { 
    multi::stream icl({"c:\\out1.txt", "c:\\out2.txt"});
    icl.attach(std::cout);

    icl << "Blah blah blah" << std::endl;
}

ご覧のとおり、これはすでにマニピュレータを受け入れています (これはだけでなく、どのマニピュレータでも動作するはずですstd::endl)。複数のファイル (fstream として開くことができるもの) に書き込みたい場合は、コンストラクターでそれらの名前を好きなだけ指定できます (もちろん、システムによって課される制限内で)。std::coutやのようにstd::cerr必ずしもファイル名を持っていないものについては、attach当初の意図どおりに使用できます。

私は、この現状に完全に満足しているわけではないことを付け加えておく必要があると思います. それを行うにはかなり深刻な書き直しが必要ですが、少し考えた後、「正しい」方法はおそらくmulti::stream'ctor を代わりに可変個引数テンプレートにすることだと思うので、次のようなことができるでしょう: multi::stream icl("c:\\out1.txt", std::cout);,そして、そのタイプに基づいて各パラメーターの使用方法を整理します。この回答を更新して、その機能をすぐに含めることができます。

2番目の質問に関する限り、基本的な考え方をカバーする別の回答を書きましたが、おそらく少し複雑すぎるため、気になる部分がシャッフルで失われる可能性があります。実際には気にしない行の長さを処理するためのかなりのロジック(ただし、必要に応じて、指定されたプレフィックスを持つ各出力行を生成します)。

于 2013-04-21T22:04:56.010 に答える
4

次のようなものが必要になる場合があります。

class OutputMan {
    std::vector<std::ostream*> m_Streams;

public:
    void attach(std::ostream *os) {
        m_Streams.push_back(os);
    }

    template <typename T>
    OutputMan &operator<<(const T &t) {

        for (int i=0; i<m_Streams.size(); i++)
            *m_Streams[i] << t;

        return *this;
    }
};

int main() {
    ofstream file("test.txt");

    OutputMan x;
    x.attach(&cout);
    x.attach(&cerr);
    x.attach(&file);

    x << "Hello" << 123;
}

簡単にするために、 を使用しstd::ostream*ました。<<私がオーバーロードしたものを受け入れるためにoperator<<

 

注:他のものと同様にOutputMan承認が必要な場合は、こちらをお読みくださいstd::endl

于 2013-04-21T19:51:17.453 に答える