将流存储在变量中以供以后使用

时间:2015-06-27 20:42:33

标签: c++ c++11 stream std cout

在我的代码中,我有一个地方,我必须将完全相同的operator<<流传递到两个不同的地方。一到ofstream,一到cout

m_logFileStream << "[" << now->tm_hour << ":" << now->tm_min << ":" << now->tm_sec << "]"
                << "[" << logLevelsStrings[(int)logline.logLevel] << "] "
                << logline.logString << endl;

        if(m_verbose)
        {
            cout << "[" << now->tm_hour << ":" << now->tm_min << ":" << now->tm_sec << "]"
                            << "[" << logLevelsStrings[(int)logline.logLevel] << "] "
                            << logline.logString << endl;
        }

m_logFileStreamofstream。如果我想改变模式,我需要在2个地方做。在这样的变量中,它会更方便地存储它:

stringstream ss;
        ss      << "[" << now->tm_hour << ":" << now->tm_min << ":" << now->tm_sec << "]"
                << "[" << logLevelsStrings[(int)logline.logLevel] << "] "
                << logline.logString << endl;

        m_logFileStream << ss;

        if(m_verbose)
        {
            cout << ss;
        } 

但由于某种原因而不是正确的输出,我得到随机的十六进制数字。我不知道我在这里做错了什么。我会帮助你!

编辑: cout << ss.str();有效但m_logFileStream << ss.str();不会为文件m_logFileStream保存任何内容。奇怪...

1 个答案:

答案 0 :(得分:1)

已经讨论了代码的直接问题:插入流导致转换运算符被触发void const*并导致打印指针值(很可能是流的地址)。修复方法是使用ss.str()ss.rdbuf()代替,可能后跟std::flush。请注意,ss.str()每次调用时都会创建std::string。如果流包含许多可能不太令人满意的数据。插入流ss.rdbuf()时也应该可以工作,它可以绕过创建额外的流。但是,在使用它两次之间,需要将插入的流设置为再次迭代序列,例如,通过寻找开始。

到目前为止修补原始设计。我建议对整体问题采用不同的设计:不是首先创建一个字符串,然后将其插入两个不同的流中,而是创建一个内部转发到内部的一个或多个流的流。创建新流的神奇功能称为流缓冲区,即从std::streambuf派生的类。

流缓冲区的简单实现可能如下所示:

#include <streambuf>
#include <algorithm>
#include <ostream>
#include <vector>

class teebuf
    : public std::streambuf
{
    char                         buffer[1024];
    std::vector<std::streambuf*> sbufs;

    int overflow(int c) {
        typedef std::streambuf::traits_type traits;
        if (!traits::eq_int_type(traits::eof(), c)) {
            *this->pptr() = traits::to_char_type(c);
            this->pbump(1);
        }
        return this->sync() == 0? traits::not_eof(c): traits::eof();
    }
    int sync() {
        bool rc(false);
        if (this->pbase() != this->pptr()) {
            std::for_each(sbufs.begin(), sbufs.end(),
                [&](std::streambuf* sb){
                    sb->sputn(this->pbase(), this->pptr() - this->pbase());
                    sb->pubsync() != -1 || (rc = false);
                });
            this->setp(buffer, buffer + sizeof(buffer) - 1);
        }
        return rc? -1: 0;
    }
public:
    teebuf() { this->setp(buffer, buffer + sizeof(buffer) - 1); }
    void add(std::ostream& out){ sbufs.push_back(out.rdbuf()); }
    void remove(std::ostream& out){
        sbufs.erase(std::remove(sbufs.begin(), sbufs.end(), out.rdbuf()),
                    sbufs.end());
    }
};

除了对输出转发的流缓冲区列表进行一些简单的管理外,该类还会覆盖两个virtual函数:

    当流缓冲区的缓冲区(使用overflow()设置)已满但另一个字符被放入缓冲区时,将调用
  1. setp()。所有这个功能都是使用为这种情况保存的额外字符(如果函数没有使用特殊值std::char_traits<char>::eof()调用)并调用sync()(见下文)。
  2. 当需要刷新当前缓冲区时,会调用
  3. sync(),例如,因为用户要求使用std::flush刷新流,或者因为缓冲区已满。
  4. 要实际使用此流缓冲区,您需要创建std::ostream并使用指向此std::streambuf的指针对其进行初始化。这类似于std::ofstream对其std::filebuf的处理方式。为了更容易创建合适的流,有必要打包它:

    class oteestream
        : private virtual teebuf
        , public std::ostream {
    public:
        oteestream()
            : teebuf()
            , std::ostream(this) {
            this->init(this);
        }
        using teebuf::add;
        using teebuf::remove;
    };
    

    假设在标题"teestream.h"中声明了此流缓冲区和自定义流,它的使用变得相当直接:

    #include "teestream.h"
    #include <fstream>
    #include <iostream>
    
    int main()
    {
        std::ofstream fout("tee.txt");
        oteestream    tee;
        tee.add(fout);
        tee.add(std::cout);
        tee << "hello, world!\n" << std::flush;
        tee.remove(std::cout);
        tee << "goodbye, world!\n" << std::flush;
    }
    

    将发送到多个流的包装的独特优势在于,您甚至不需要处理在多个位置转发字符串:您只需写入流并刷新(I&amp; #39; m有点against the use of std::endl触发冲洗。)