C++ 如何在C++;它接收所有不同的输出流,如cout、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

我试图实现一个记录器,它可以注册多个流,如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 <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