std :: facet对象上的可变标志

时间:2015-02-02 05:59:11

标签: c++

我尝试创建一些I / O操纵器,以允许用户修改自定义类型的输出格式。

假设我有一个Foo对象:我可能想要以一种漂亮的,人类可读的格式输出它(漂亮的打印),或者我可能想要以浓缩的形式打印它以在序列化时节省空间

所以,拥有像condensedpretty之类的自定义I / O操纵器来修改方面的内部标志会很好,所以我可以这样说:

Foo f;

...

std::cout << pretty << f; // output human-readable format
std::cout << condensed << f; // output condensed format

我经常遇到的问题是,一旦创建了一个构面对象,它只能在以后使用std::use_facet检索,它会返回一个 const 引用。这意味着我以后无法修改任何内部方面标志。

考虑一个简单的方面:

class my_facet : public std::locale::facet
{
    public:

    my_facet() : m_pretty(false), m_condensed(false)
    { }

    void set_pretty(bool b) { m_pretty = b; }

    void set_condensed(bool b) { m_condensed = b; }

    static std::locale::id id;

    private:

    bool m_pretty;
    bool m_condensed;
};

然后我可以创建I / O操纵器,如:

template <class CharT, class Traits>
inline std::basic_ostream<CharT, Traits>& pretty(std::basic_ostream<CharT, Traits>& os)
{
    my_facet<CharT>* facet = new my_facet();
    facet->set_pretty(true);
    facet->set_condensed(false);
    std::locale loc = std::locale(os.getloc(), facet);
    os.imbue(loc);
    return os;
}

这很好用......但是如果我想允许用户指定其他标志或格式化选项,比如说,indentation选项允许用户指定要缩进的空格数,例如这样:

std::cout << pretty << indent(4) << f;

问题是每个I / O操纵器都必须重新创建构面对象,因此先前的标志集会丢失。原因是无法访问现有构面的非const 引用。

我想说:

template <class CharT, class Traits>
inline std::basic_ostream<CharT, Traits>& operator << (std::basic_ostream<CharT, Traits>& os,
    const indent& ind)
{
    const my_facet<CharT>& facet = std::use_facet<my_facet>(os.getloc()); 
    facet.set_indentation(ind.value()); // Error: facet is const!
    return os;
}

...但当然,这不会奏效,因为facetconst。我能看到的唯一方法是制作所有内部标志mutable,这是荒谬的。

所以,我感觉到我做错了。似乎没有任何方法可以获得对现有方面的非const引用,所以我认为我以错误的方式处理这一切。

那么,这种事情通常是如何实现的?如何编写可以链接在一起设置不同标志的I / O操纵器,例如:

std::cout << pretty << indent(3) << etc ...

1 个答案:

答案 0 :(得分:2)

存储自定义格式化状态的可接受方式是使用std::ios_base::xalloc分配的内存。例如(删节,live demo with full code here):

class MyFancyManipulator
{
    static int myidx;
    int st;
  public:
    MyFancyManipulator(int st) : st(st) {};
    template <typename Ch, typename Tr> friend
        std::basic_ostream<Ch, Tr>& operator<< (std::basic_ostream<Ch, Tr>& str,
                const MyFancyManipulator& man) {
            //
            // Transfer state from the manipulator to the stream
            //
            str.iword(MyFancyManipulator::myidx) = man.st;
            return str;
        }
};

// Allocate index for the state variable
// This is thread safe per C++14 standard
int MyFancyManipulator::myidx = std::ios_base::xalloc();


// ... In some custom operator<<

   int state = str.iword(MyFancyManipulator::myidx);