如何确定输出流链是否已结束?

时间:2014-01-20 23:33:58

标签: c++ templates c++11 stream

我想要实现的目标是什么?

如何查找流链是否已结束?看看下面的函数(在这个问题中所有这些函数都在一个LogRouter类中):

template<typename First, typename... Rest>
void log(const LogLevel &level_, First first_, Rest... rest_) {
    sstream << first_ << " ";
    log(level_, rest_...);
}

void log(const LogLevel &level_) {
    for(auto &route : routes)
        route->stream() << sstream.str() << std::endl;

    sstream.clear();
    sstream.str("");
}

我希望在上面使用流来实现完全相同的功能。因此,当我到达流的末尾时,它需要将最终数据发送到路由而不是使用

router.log(LogLevel::Alert, "test stream", 15);

我希望能够使用

router.log(LogLevel::Alert) << "test stream " << 15;

我尝试了什么:

  • std::ostream运算符重载不接受压缩变量。

  • 逐个浏览每一个传递的值。如下所示:

     struct LogEnd {};
    
     static LogEnd end() { return LogEnd; }
    
     template<typename T> LogRouter &operator<<(const T &value) {
         sstream << value;
         return *this;
     }
    
     LogRouter &log(const LogLevel &level_) {
         currentLogLevel = level_; //had to add another variable
         return *this;
     }
    
     void operator<<(const LogEnd &end) {
        for(auto &route : routes)
            route.stream() << sstream.str() << std::endl;
        currentLogLevel = LogLevel::None;
    }
    

    这给了我想要的语法,但我需要在每个结尾处调用额外的LogRouter::end()

     router.log(LogLevel::Alert) << "test stream " << 15 << LogRouter::end();
    

    我也有std::endl的语法,但最好是在最后没有任何内容的情况下调用它。

问题

有没有办法知道流链的结束。类似于使用递归可变参数模板函数时可以执行的操作。

1 个答案:

答案 0 :(得分:6)

您可以将有趣的逻辑放入流的析构函数中。显然,我也会妥善处理流而不是烹饪一些看起来像流的东西,但实际上不是流:

#include <iostream>
#include <sstream>
#include <string>

class logstream
    : private virtual std::stringbuf
    , public std::ostream {
    std::string level;
public:
    logstream(std::string l)
        : std::ostream(this)
        , level(l) {
    }
    logstream(logstream&& other)
    : std::stringbuf(std::move(other))
    , std::ostream(std::move(other))
    , level(std::move(other.level)) {
        this->rdbuf(0);
    }
    ~logstream() {
        std::cout << "do something interesting here("
                  << this->level<< ", " << this->str() << ")\n";
    }
};

logstream trace() {
    return logstream("trace");
}

int main()
{
    trace() << "hello, world";
}

使用的流缓冲区(在这种情况下为std::stringbuf,但它也可以是自定义流缓冲区)成为基类,以便在std::ostream之前构建它。原则上它意味着是数据成员,但数据成员是在基类之后构造的。因此,它改为private基类。

事实证明std::ostream有一个virtual基类(std::ios)会导致std::ostream仍然在std::stringbuf之前构建,如果正常的话用于std::stringbuf的继承。使用virtual继承并使std::stringbuf成为第一个基类,确保它首先被构造。