是的,我甚至不确定如何正确地制定这个;我觉得这是一个涉及的问题。我确定有人可以帮助我。
这就是我想要做的事情:
有一个我可以发送内容的课程,就像这样。
icl << "Blah blah blah" << std::endl;
我希望能够继承std :: basic_ostream的.attach()类。
然后,这些类可以按照自己的方式格式化输出。有人可能会添加一个时间戳并写入日志,另一个可能会将其写入控制台,另一个可能会在游戏中显示它。
有人想让我开始朝着正确的方向前进吗?这就是我几乎拥有的想法。
#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并创建一个更改输出的类,例如,在其开头添加时间戳?我也希望这个类输出到一个文件。
答案 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::cout
和std::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。