我想定义一个类 MyStream 以便:

MyStream myStream;
myStream << 1 << 2 << 3 << std::endl << 5 << 6 << std::endl << 7 << 8 << std::endl;

给出输出

[blah]123
[blah]56
[blah]78

基本上,我想在前面插入一个“[blah]”,然后在每个之后插入 非终止 std::endl?

这里的困难不是逻辑管理,而是检测和重载处理 std::endl. 。有没有一种优雅的方法来做到这一点?

谢谢!

编辑:我不需要逻辑管理方面的建议。我需要知道如何检测/过载打印 std::endl.

有帮助吗?

解决方案

您需要做的是编写自己的流缓冲区:
当流缓冲区被刷新时,您将输出前缀字符和流的内容。

以下内容有效,因为 std::endl 会导致以下结果。

1) 将“ ”添加到流中。
2) 在流上调用flush()
2a) 这会在流缓冲区上调用 pubsync()。
2b) 这会调用虚拟方法sync()
2c) 重写此虚拟方法来完成您想要的工作。

#include <iostream>
#include <sstream>

class MyStream: public std::ostream
{
    // Write a stream buffer that prefixes each line with Plop
    class MyStreamBuf: public std::stringbuf
    {
        std::ostream&   output;
        public:
            MyStreamBuf(std::ostream& str)
                :output(str)
            {}
            ~MyStreamBuf() {
                if (pbase() != pptr()) {
                    putOutput();
                }
            }

        // When we sync the stream with the output. 
        // 1) Output Plop then the buffer
        // 2) Reset the buffer
        // 3) flush the actual output stream we are using.
        virtual int sync() {
            putOutput();
            return 0;
        }
        void putOutput() {
            // Called by destructor.
            // destructor can not call virtual methods.
            output << "[blah]" << str();
            str("");
            output.flush();
        }
    };

    // My Stream just uses a version of my special buffer
    MyStreamBuf buffer;
    public:
        MyStream(std::ostream& str)
            :std::ostream(&buffer)
            ,buffer(str)
        {
        }
};


int main()
{
    MyStream myStream(std::cout);
    myStream << 1 << 2 << 3 << std::endl << 5 << 6 << std::endl << 7 << 8 << std::endl;
}

> ./a.out
[blah]123 
[blah]56 
[blah]78
>

其他提示

您的超载运算符 MyStream 类必须设置 previous-printed-token-was-endl 标志。

然后,如果打印下一个对象, [blah] 可以插入到它的前面。

std::endl 是一个函数,获取并返回对的引用 std::ostream. 。要检测它已转移到您的流中,您必须重载 operator<< 在你的类型和这样的函数之间:

MyStream& operator<<( std::ostream&(*f)(std::ostream&) )
{
    std::cout << f;

    if( f == std::endl )
    {
        _lastTokenWasEndl = true;
    }

    return *this;
}

原则上同意尼尔的观点。

您想要更改缓冲区的行为,因为这是扩展 iostream 的唯一方法。 endl 做这个:

flush(__os.put(__os.widen('\n')));

widen 返回单个字符,因此您不能将字符串放在那里。 put 来电 putc 这不是一个虚拟函数,只是偶尔挂钩 overflow. 。您可以拦截在 flush, ,它调用缓冲区的 sync. 。您需要拦截并更改所有换行符 overflow编辑或手动 synced 并将它们转换为您的字符串。

设计一个覆盖缓冲区类很麻烦,因为 basic_streambuf 期望直接访问其缓冲存储器。这会阻止您轻松地将 I/O 请求传递给预先存在的 basic_streambuf. 。您需要冒险并假设您知道流缓冲区类,并从中派生。(cincout 不保证使用 basic_filebuf, ,据我所知。)然后,只需添加 virtual overflowsync. 。(参见§27.5.2.4.5/3 和 27.5.2.4.2/7。)执行替换可能需要额外的空间,因此请小心提前分配。

- 或者 -

只需声明一个新的 endl 在你自己的命名空间中,或者更好的是,一个不被调用的操纵器 endl 根本!

而不是试图改变 std::endl, ,您可能应该创建一个过滤streambuf来完成这项工作。詹姆斯·坎泽 (James Kanze) 有一个 例子 展示如何在每个输出行的开头插入时间戳。只需进行少量修改即可将其更改为您想要在每行上添加的任何前缀。

我使用函数指针。对于不习惯 C 语言的人来说,这听起来很可怕,但在大多数情况下它的效率要高得多。这是一个例子:

#include <iostream>

class Foo
{
public:
    Foo& operator<<(const char* str) { std::cout << str; return *this; }
    // If your compiler allows it, you can omit the "fun" from *fun below.  It'll make it an anonymous parameter, though...
    Foo& operator<<(std::ostream& (*fun)(std::ostream&)) { std::cout << std::endl; }
} foo;

int main(int argc,char **argv)
{
    foo << "This is a test!" << std::endl;
    return 0;
}

如果您确实愿意,可以检查 endl 的地址以确认您没有获得其他 void/void 函数,但我认为在大多数情况下这是不值得的。我希望这有帮助。

你无法改变 std::endl - 顾名思义,它是 C++ 标准库的一部分,并且其行为是固定的。当流收到 end of line 时,您需要更改流本身的行为。就我个人而言,我认为这不值得付出努力,但如果你想冒险进入这个领域,我强烈建议你阅读这本书 标准 C++ IOStream 和区域设置.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top