输出到几个ostreams的类(文件和控制台)

时间:2013-04-21 19:38:05

标签: c++ inheritance ostream

是的,我甚至不确定如何正确地制定这个;我觉得这是一个涉及的问题。我确定有人可以帮助我。

这就是我想要做的事情:

  1. 有一个我可以发送内容的课程,就像这样。

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

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

  4. 有人想让我开始朝着正确的方向前进吗?这就是我几乎拥有的想法。

    #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并创建一个更改输出的类,例如,在其开头添加时间戳?我也希望这个类输出到一个文件。

2 个答案:

答案 0 :(得分: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::coutstd::cerr之类的内容,您可以按原定使用attach

我想我应该补充一点,我对此并不完全满意。这需要一些相当严肃的重写,但经过一些思考,我认为“正确”的方式可能是multi::stream的ctor而不是变量模板,所以你可以执行类似:multi::stream icl("c:\\out1.txt", std::cout);之类的操作,它会根据类型选择如何使用每个参数。我可能会在一段时间内更新此答案以包含该功能。

就第二个问题而言,我写了another answer来涵盖基本思想,但可能有点过于复杂,所以你关心的部分可能会在洗牌中丢失,所以说 - 它有很多逻辑来处理你并不真正关心的行长度(但确实产生了具有指定前缀的每个输出行,如你所愿)。

答案 1 :(得分: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以及其他内容,请阅读here