如何实现自动添加分隔符的自定义粘性操纵器? [英] How to implement custom sticky manipulator that automatically adds separators?

查看:141
本文介绍了如何实现自动添加分隔符的自定义粘性操纵器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Python中的打印函数会使用可自定义的分隔符自动分隔其参数。有没有什么方法可以通过使用流操纵器在C ++中模拟这种行为?

The print function in Python automatically separates its arguments with a customisable separator. Is there any way to emulate this behavior in C++ by using stream manipulators?

也就是下面的C ++代码:

That is, the following C++ code:

std::cout << custom::sep(", ") << 1 << "two" << 3 << std::endl;

应该类似于以下Python代码:

Should work similar to the following Python code:

print(1, "two", 3, sep=", ")

所需的输出为:

1, two, 3

如何实现 custom :: sep ?它似乎比你的标准自定义操纵器有点更棘手,因为它不能只改变流上的下一个项目,如此处此处 a>。它应该是粘的,直到下一个 custom :: sep std :: endl 。此外,它不能只处理数字或某些类型,例如此处

How would I go about implementing custom::sep? It seems a bit more tricky than your standard custom manipulator, because it cannot just change the next item on the stream, like here or here. It should be sticky until the next custom::sep or std::endl. Furthermore, it cannot just work on numbers or certain types, like here. It should work with any streamable type.

推荐答案


您发布的解决方案的问题是依赖于定制整数使用facets进行格式化的方式。不幸的是,我不认为有相应的设施可以为任意类型工作。

The issue with the solution you posted is that it relies on customizing the way integers get formatted using facets. Unfortunately, I do not think there is a corresponding facility which would work for arbitrary types.

有。你可以使用流的底层缓冲区来获得你想要的。缓冲区是字符序列最终被收集用于维护的地方。以下代码使流缓冲区保存对您要使用其字符序列的对象的引用。我们设置 std :: ios_base :: unitbuf 格式标志,以便在每个输出操作上刷新流(所以我们可以将分隔符添加到结尾)。

There is. You can use utilize the underlying buffer of the stream to get what you want. The buffer is where the character sequence is eventually gathered for maintenance. The following code makes a stream buffer that holds a reference to the object whose character sequence you wish to use. We set the std::ios_base::unitbuf format flag so that the stream is flushed on each output operation (so we can add the separator to the end).

通过扩展,它还允许您卸载分隔符,并确保在此过程中没有内存泄漏:

By extension, it also allows you to uninstall the separator and makes sure that no memory is leaked in the process:

#include <iostream>

namespace custom
{
    struct sep_impl
    {
        sep_impl(std::string const& separator);
        std::string separator;
    };

    sep_impl sep(std::string const& str)
    {
        return sep_impl(str);
    }

    std::ostream& nosep(std::ostream& os);
}

int separatorEnabled()
                      { static int idx = std::ios_base::xalloc(); return idx; }
int getSeparator()    { static int idx = std::ios_base::xalloc(); return idx; }

struct custom_separator : std::streambuf
{
public:
    custom_separator(std::ostream& _stream) : stream(_stream)
    { }

    int_type overflow(int_type c)
    {
        return stream.rdbuf()->sputc(c);
    }

    int sync()
    {
        if (stream.iword(separatorEnabled()))
        {
            void*& p = stream.pword(getSeparator());
            stream << *static_cast<std::string*>(p);
            return 0;
        }
        return stream.rdbuf()->pubsync();
    }
private:
    std::ostream& stream;
};

void cleanup(std::ios_base::event evt, std::ios_base& str, int idx)
{
    if (str.iword(separatorEnabled()) && evt == std::ios_base::erase_event)
    {
        void*& p = str.pword(idx);
        delete static_cast<std::string*>(p);
        str.iword(separatorEnabled()) = false; 
    }
}

std::ostream& set_separator(std::ostream& os, const custom::sep_impl& manip)
{
    if (!os.bad())
    {
        os.pword(getSeparator()) = new std::string(manip.separator);
        os.register_callback(cleanup, getSeparator());
    }

    return os;
}

std::ostream& operator<<(std::ostream& os, const custom::sep_impl& manip)
{
    std::ostream* p = os.tie();
    if (p && !p->iword(separatorEnabled())
    {
        set_separator(*p, manip);
        p->iword(separatorEnabled()) = true;
    }

    return os << std::unitbuf;
}

namespace custom
{
    sep_impl::sep_impl(std::string const& _sep) : separator(_sep) { }

    std::ostream& nosep(std::ostream& os)
    {
        cleanup(std::ios_base::erase_event, *os.tie(), getSeparator());
        os.tie(nullptr);
        return os << std::nounitbuf;
    }

    void install_separator(std::ostream& o1, std::ostream& o2)
    {
        static custom_separator csep(o2);
        o1.rdbuf(&csep);
        o1.tie(&o2);
    }
}

int main()
{
    std::ostream os(nullptr);
    custom::install_separator(os, std::cout);

    os << custom::sep(", ") << 4 << 2 << custom::nosep;
}



我确定还有改进的余地,所以如果任何人有任何建议他们非常感激。

I'm sure there is also room for improvement, so if anyone has any suggestions they are very much appreciated.

< kbd>实例

这篇关于如何实现自动添加分隔符的自定义粘性操纵器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆