2014-12-06 100 views
0

我想要在cout上重定向的每一行的开头都有一个前缀的ostream; 我试试这个:带前缀的C++ cout

#include <iostream> 
#include <thread> 
class parallel_cout : public std::ostream 
{ 
public: 
parallel_cout(std::ostream& o):out(o){} 

template <typename T> 
std::ostream& operator<< (const T& val) 
{ 
    out << "prefix " << val; 
    return *this; 
} 

std::ostream& out; 

}; 

int main() 
{ 
parallel_cout pc(std::cout); 
pc<<"a\nb"<<"c\n"; 
} 

,但我在输出

prefix a 
b 

无C。为什么这个?

+0

通常,从std :: stream派生它不是一个好主意 - 它没有虚拟析构函数,并且代码显示了输出操作在派生类中的效果如何,因为您正在返回对基类的引用。我会尝试扩展到一个完整的答案,除非有人打我:) – 2014-12-06 20:52:08

+2

您的模板应该返回'parallel_cout&',而不是通用'ostream&'。 – Deduplicator 2014-12-06 20:52:15

+2

@martin_pr'std :: ostream'有一个虚拟构造函数(继承自'std :: ios_base')。此外,'std :: fstream'和'std :: stringstream'也是派生类,但它们工作正常。你想从IOStreams基类派生的主要原因是环绕自定义的'std :: streambuf',这是具体的流类。 – 0x499602D2 2014-12-06 20:54:29

回答

7

的方式修改的std::ostream行为是通过重载任何输出运算符!相反,您从std::streambuf派生类并取代virtual函数overflow()sync()。在你的情况下,你可能会创建一个过滤流缓冲区,也就是说,你可以将另一个std::streambuf作为参数,并向该流缓冲区中写入一个修改过的字符流。

下面是一个简单的例子:

#include <iostream> 

class prefixbuf 
    : public std::streambuf 
{ 
    std::string  prefix; 
    std::streambuf* sbuf; 
    bool   need_prefix; 

    int sync() { 
     return this->sbuf->pubsync(); 
    } 
    int overflow(int c) { 
     if (c != std::char_traits<char>::eof()) { 
      if (this->need_prefix 
       && !this->prefix.empty() 
       && this->prefix.size() != this->sbuf->sputn(&this->prefix[0], this->prefix.size())) { 
       return std::char_traits<char>::eof(); 
      } 
      this->need_prefix = c == '\n'; 
     } 
     return this->sbuf->sputc(c); 
    } 
public: 
    prefixbuf(std::string const& prefix, std::streambuf* sbuf) 
     : prefix(prefix) 
     , sbuf(sbuf) 
     , need_prefix(true) { 
    } 
}; 

class oprefixstream 
    : private virtual prefixbuf 
    , public std::ostream 
{ 
public: 
    oprefixstream(std::string const& prefix, std::ostream& out) 
     : prefixbuf(prefix, out.rdbuf()) 
     , std::ios(static_cast<std::streambuf*>(this)) 
     , std::ostream(static_cast<std::streambuf*>(this)) { 
    } 
}; 

int main() 
{ 
    oprefixstream out("prefix: ", std::cout); 
    out << "hello\n" 
     << "world\n"; 
} 

流缓冲概念性地保持一个内部缓冲器,该缓冲器是,然而,没有设置在上述的例子。每次没有将字符写入输出缓冲区的空间时,virtual函数overflow()被调用一个字符(也可以使用通常用于刷新缓冲区的特殊值std::char_traits<char>::eof()来调用)。由于没有缓冲区,每个角色都会调用overflow()。所有这些功能的作用是查看是否需要写入前缀,如果是,则写入prefix。如果写入换行'\n',则该函数会记住如果写入另一个字符,则需要编写prefix。然后它只是将字符的写入转发给基础流缓冲区。

virtual功能sync()用于同步流与其外部表示。对于保留缓冲区的流缓冲区,它确保写入任何缓冲区。由于prefixbuf并不真正保留一个缓冲区,所以它只需要通过调用pubsync()sync()请求委托给基础流缓冲区。

+0

我不知道这些方法(同步和溢出),你知道一个很好的教程吗? – volperossa 2014-12-06 21:21:14

+1

IOStreams库在Nicolai Josuttis的“C++标准库”中进行了解释,很明显,解释是_excellent_(尽管我特别写了创建流缓冲区的原始版本)。还有Angelika Langer和Klaus Kreft的“IOStreams and Locales”,但这是一本专门介绍I/O的书。否则,如果您在Google上查看“streambuf”和“James Kanze”或“Dietmar Kuehl”,则应该找到大量资源。 – 2014-12-06 21:25:38

0

您的输出运算符正在返回对基类(std::ostream)的引用,这意味着下一次调用operator <<将不会使用您的实现。

改变你的输出运营商这应该解决您的问题:

template <typename T> 
parallel_cout& operator<< (const T& val) 
{ 
    out << "prefix " << val; 
    return *this; 
} 
3

正如迪特马尔说,你要的是你自己的流缓冲,某事,这一般顺序:

#include <streambuf> 
#include <iostream> 

class prefixer: public std::streambuf { 
public: 
    prefixer(std::streambuf* s): sbuf(s) {} 
    ~prefixer() { overflow('\n'); } 
private: 
    typedef std::basic_string<char_type> string; 

    int_type overflow(int_type c) { 

     if (traits_type::eq_int_type(traits_type::eof(), c)) 
      return traits_type::not_eof(c); 
     switch (c) { 
     case '\n': 
     case '\r': { 
      prefix = "[FIX]"; 
      buffer += c; 
      if (buffer.size() > 1) 
       sbuf->sputn(prefix.c_str(), prefix.size()); 
      int_type rc = sbuf->sputn(buffer.c_str(), buffer.size()); 
      buffer.clear(); 
      return rc; 
     } 
     default: 
      buffer += c; 
      return c; 
     } 
    } 

    std::string prefix; 
    std::streambuf* sbuf; 
    string buffer; 
}; 

要使用这个,你从其他流缓冲创建它的一个实例(即定义了它要写入),然后(通常)使用这个streambuf创建一个ostream。然后,您可以写入该ostream,并将您的前缀写入每行输出。例如:

int main() { 
    prefixer buf(std::cout.rdbuf()); 

    std::ostream out(&buf); 

    out << "Test\n"; 
} 
+0

不错的努力,虽然我更喜欢我的版本:我认为它更简单,另一个它不会在最后一个换行符后创建尾随前缀。它也是第一行的前缀,没有虚假的换行符。最后,它通过刷新到底层流缓冲区。我意识到你的版本是缓冲的,但不会刷新'sync()'这会造成一些伤害。尽管我认识到不缓冲流缓冲区会带来潜在的性能问题,但我的版本并未进行简化。 – 2014-12-06 21:22:05

1

或者,您可以通过语法更改编写函数。

template<typename T> 
void print(T content) 
{ 
    cout<<whatever your prefix is; 
    cout<<content; 
}