3

是的,我什至不确定如何正确表述这一点;我觉得这个问题有点牵强。我相信有人可以在这里帮助我。

这就是我想要做的:

  1. 有一个我可以发送东西的班级,就像这样。

    icl << "Blah blah blah" << std::endl;
    
  2. 我希望能够 .attach() 继承 std::basic_ostream 的类。

  3. 然后,这些类将能够以自己的方式格式化输出。一个可能会添加时间戳并写入日志,另一个可能会将其写入控制台,另一个可能会在游戏中显示它。

有人愿意让我朝着正确的方向开始吗?这是我非常有的想法。

#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)。如果您想写入多个文件(可以/可以作为 fstreams 打开的东西),您可以在构造函数中指定任意数量的这些名称(当然,在您的系统施加的限制范围内)。对于您不一定有文件名之类的东西,您可以std::cout按照最初的意图使用。std::cerrattach

我想我应该补充一点,我对现状并不完全满意。这样做需要进行一些相当认真的重写,但经过一番思考,我认为“正确”的方法可能是让multi::streamctor 成为可变参数模板,因此您可以执行以下操作multi::stream icl("c:\\out1.txt", std::cout);:它会根据参数的类型整理出如何使用每个参数。我可能会在不久的将来更新此答案以包含该功能。

至于第二个问题,我已经写了另一个涵盖基本思想的答案,但可能有点过于复杂,所以你关心的部分可能会在洗牌中迷失,可以这么说 - 它有相当多的逻辑来处理您并不真正关心的行长度(但确实会产生带有指定前缀的每个输出行,就像您想要的那样)。

于 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 回答