C++ 如何在C++;它接收所有不同的输出流,如cout、ostringstream和ofstream
我试图实现一个记录器,它可以注册多个流,如ostringstream、ofstream等。我试图实现这样的功能C++ 如何在C++;它接收所有不同的输出流,如cout、ostringstream和ofstream,c++,c++11,vector,ostream,C++,C++11,Vector,Ostream,我试图实现一个记录器,它可以注册多个流,如ostringstream、ofstream等。我试图实现这样的功能 void register_stream(std::ostream& a); 向量如下所示 std::vector<std::ostream> streams; void logger::register_stream(std::ostream &a)` { streams.push_back(a); } template <typ
void register_stream(std::ostream& a);
向量如下所示
std::vector<std::ostream> streams;
void logger::register_stream(std::ostream &a)`
{
streams.push_back(a);
}
template <typename T>
void logger::operator<<(T const& value)
{
for (auto stream : streams)
{
(stream) << value;
}
}
std::矢量流;
寄存器流和运算符重载如下所示
std::vector<std::ostream> streams;
void logger::register_stream(std::ostream &a)`
{
streams.push_back(a);
}
template <typename T>
void logger::operator<<(T const& value)
{
for (auto stream : streams)
{
(stream) << value;
}
}
void记录器::寄存器\u流(std::ostream&a)`
{
流。推回(a);
}
模板
void logger::operator您不能存储流的副本,它们是不可复制的。您必须存储s或指针
class logger {
std::vector<std::reference_wrapper<std::ostream>> streams;
public:
void register_stream(std::ostream &stream) {
streams.emplace_back(stream);
}
template <typename T>
void operator<<(T const &value) {
for (auto &stream : streams) { // note the '&'
stream.get() << value;
}
}
};
类记录器{
std::矢量流;
公众:
无效寄存器_流(标准::ostream&流){
溪流。向后安置(溪流);
}
模板
void操作符ostream
对底层的streambuf
进行格式化和写入。因此,当您多次使用操作符时,它会不必要地对同一输入进行多次格式化。一种更优的方法是格式化一次,然后将格式化后的输出复制到多个底层的码流/code>中使用未格式化的输出函数。/p
pIt使用codestd::ostream/code接口很方便,因此您可以将其传递给需要codestd::ostream/code接口的现有函数。/p
p您基本上需要一个自定义的codestreambuf/代码实现。从头开始编写代码是一个很好的学习经验,但由于有点难以理解和正确实现,因此很繁琐且容易出错。请改用。/p
p工作示例:/p
预编码#包括boost/iostreams/stream.hpp
#包含算法
#包括iostream
#包含向量
结构多链接{
使用char_type=char;
结构类别
:boost::iostreams::sink\u标记
,boost::iostreams::flushable_标记
{};
std::vectorstd::ostream*sinks;
std::streamsize写入(字符常量*s,std::streamsize n){
用于(自动接收器:接收器)
接收器写入(s,n);
返回n;
}
bool flush(){
用于(自动接收器:接收器)
水槽->冲洗();
返回true;
}
void add_sink(标准::ostream&s){
下沉。推回(s);
}
清除水槽的空隙(标准::ostream&s){
接收器擦除(标准::移除(接收器开始(),接收器结束(),&s),接收器结束());
}
};
int main(){
多墨水池;
boost::iostreams::流(sink);
流->添加_接收器(标准::cout);
流->添加水槽(标准::cerr);
stream您有几个概念问题需要解决:
std::cout
是一个全局对象,但std::ostringstream
和std::ofstream
是类型。将它们作为可互换的输出进行讨论是一个类别错误
std::cout
是一个具有程序生存期的全局对象,但是您创建的任何std::ofstream
实例可能具有不同的生存期。您需要某种方法来判断其生存期是否会在您的记录器之前结束(这与cout
无关,除非您的记录器也具有程序生存期),或让记录器知道它对流的生命周期负责
拥有std::vector streams
无法工作,因为:
它按值复制流,这是明确禁止的(请参阅已删除的复制构造函数)
即使它被允许,它也会因为这个原因而被打破
对于这些问题,Maxim的答案是好的,但没有解决流的生存时间问题——如果这些都不是问题(您很乐意静态地保证每一个注册的流都会比记录器的生存时间长),那么这是一个很好的解决方案
如果您确实需要一些额外的支持来管理对象生命周期,那么您需要一些更详细的支持—例如,存储代理对象,以知道记录器是否拥有给定的流。“如果您确实需要一些额外的支持来管理对象生命周期,那么您需要一些更精细的东西-例如,存储代理对象,以知道记录器是否拥有给定的流。”–无用
我当前项目中此问题的当前解决方案如下所示:
using ostream_deleter = std::function<void(std::ostream *)>;
using ostream_ptr = std::unique_ptr<std::ostream, ostream_deleter>;
如果您想要更多的自文档化语法,还有一个选项
不幸的是,仍然有一个问题可能是内部重定向的流。实际上,C++支持将输出重定向到(或输入)任何流到不同的流缓冲区,例如
。
std::ofstream logStream{"/tmp/my.log"};
const auto oldBuf = std::cout.rdbuf(logStream.rdbuf());
std::cout << "Hello World.\n"; // output redirected to /tmp/my.log
std::cout.rdbuf(oldBuf); // undo redirection
std::ofstream logStream{/tmp/my.log};
const auto oldBuf=std::cout.rdbuf(logStream.rdbuf());
Std::Cuffo>代码> oSt**/COD>指针,如<代码> STD::向量流;< /COD>。您试图按值存储它们,但流对象不能复制。@ IGORANTTIDNIK会带来终身问题。它们必须确保记录器不超过流。请通过代码来澄清。我是C++新手。对不起。N.NaNaOLLID。r要求它支持std::cout
抛出任何可以管理所有元素生存期的解决方案。这意味着必须单独管理生存期问题,也许可以使用一个有条件地管理生存期的包装器。您需要解决您的所有权问题。如果列表将拥有并销毁流恩,你可以将它作为流的唯一ptr向量。问题是,通过设计,流不是正确的虚拟流,因此如果你有不同类型的流(文件流、标准流等),你还需要知道如何销毁(或者不麻烦)每个。我想您可以根据流的真实类型使用销毁方法绑定每个流,并将其存储在列表中。您可以只存储一个指针,这就是reference\u wrapper
为您所做的。记录器::~logger(){string_stream@user5856076不显式调用记录器的析构函数。另外,flush和close也没有用,它们由ofstream的析构函数调用。“你不能按值存储流,它们是不可复制的。”–这只适用于多态情况。对于静态类型的ca
ostream_deleter d{std::default_delete<std::ostream>{}};
ostream_ptr fileStream{new std::ofstream{"/tmp/example.foo"}, std::move(d)};
ostream_ptr coutStream{&std::cout, [](std::ostream &) {}};
std::ofstream logStream{"/tmp/my.log"};
const auto oldBuf = std::cout.rdbuf(logStream.rdbuf());
std::cout << "Hello World.\n"; // output redirected to /tmp/my.log
std::cout.rdbuf(oldBuf); // undo redirection