【问题标题】:Temporarily override output stream behavior临时覆盖输出流行为
【发布时间】:2016-06-11 11:35:08
【问题描述】:

假设我有一个要打印到 ostream 的字符向量(或只是一个迭代器对)。现在,我不只是希望它打印 - 我希望它具有特定类型的间距,我想要 char 值的不同表示,而不仅仅是将它们脱口而出到终端(例如,如果它是可打印的,则 char 和转义的 2 -数字或十六进制代码。

现在我不想编写超出最低限度的自定义代码 - 即只是一点胶水和

void foo(ostream& os, char c) {
    os << c << ' ' << as_hex(c);
}

(这就像 operator

我不想在 vecotr 上循环,并且对于每个字符手动调用 abve,我希望它的行为和看起来好像我只是在拥有 后将我的向量通过管道传输到我的输出流暂时改变了后者的属性。但有时我希望流恢复正常行为,即使对于相同的向量也是如此。

这可以合理/惯用地完成吗?

【问题讨论】:

    标签: c++ iostream pretty-print ostream idioms


    【解决方案1】:

    我假设你知道运算符重载,你的主要问题是如何告诉运算符使用哪个格式化选项,即如何在流中存储任意信息。

    任何从std::ios_base(所有标准流类)派生的东西都有.iword()方法,它从一些内部存储返回对long的引用和.pword(),它返回对void*的引用。可以通过调用std::ios_base::xalloc()获取这些调用的唯一索引

    使用流存储改变输出操作行为的示例:

    #include <iostream>
    #include <vector>
    
    const int vprint_index = std::ios_base::xalloc();
    
    std::ostream& operator<<(std::ostream& out, std::vector<char> vec)
    {
        if (out.iword(vprint_index) == 0) {
            for(char c: vec)
                out << c;
            return out;
        }
        out.iword(vprint_index) = 0;
        out << '{';
        bool first = true;
        for(char c: vec) {
            if(!first)
                out << ", ";
            first = false;
            out << c;
        }
        return out << '}';
    }
    
    int main()
    {
        std::vector<char> vector {'f', 'o', 'o'};
        std::cout << vector << '\n';
        std::cout.iword(vprint_index) = 1;
        std::cout << vector << '\n';
        std::cout << vector << '\n';
    }
    


    {f, o, o}
    呵呵

    【讨论】:

    • 这并不能解决问题,因为我需要 临时 - 我需要能够打开或关闭它。
    • @einpoklum 完成后添加std::cout.iword(vprint_index) = 0;。如果您只需要它在一项操作(非粘性机械手)上工作,请让操作员自行清除。
    • @Revolver_Ocelot “临时覆盖”流格式可能意味着以下两种情况之一:一系列一次性操作(流在每次调用后自动恢复 - 我的解释),或有状态更改(显然是你的)。我赞成您的回答,因为它正确地找到了存储状态的位置(用于后一种解释)。由于问题措辞不当且不清楚,因此无法确定最初的意图(这并不重要)。
    【解决方案2】:

    对于暂时的重载,我认为做一些像iomanip 这样的东西会很好。这将允许做类似的事情

    int main() 
    { 
        std::vector<char> v{'a', 'b'}; 
        /* v should be printed using foo_vector_print, perhaps
        *    next invocation should use a different format. */
        std::cout << foo_vector_print() << v << std::endl; 
    }   
    

    这个想法是有某种形式的东西:

     stream << manip << vector;
    

    在哪里

    • stream &lt;&lt; manip 将返回一个存储对流的引用的类的对象。
    • ... &lt;&lt; vector 将使用特定格式打印,然后返回对原始流的引用。

    以下程序展示了如何做到这一点:

    #include <iostream>
    #include <vector>
    
    
    namespace detail
    {
    
    // A dummy class in detail namespace, simply records the original ostream object.
    struct foo_vector_printer_imp
    {
        std::ostream *m_os;
    };
    
    };
    
    
    // This particular implementation simply prints out the vectors size.
    std::ostream &operator<<(detail::foo_vector_printer_imp imp, const std::vector<char> &v)
    {
        *imp.m_os << v.size();
    
        return *imp.m_os;
    }
    
    
    struct foo_vector_print{};
    
    
    /* The result of piping a foo_vector_print into an ostream, 
    *    is a foo_vector_printer_imp object recording the ostream */
    detail::foo_vector_printer_imp operator<<(std::ostream &os, const foo_vector_print &)
    {
        detail::foo_vector_printer_imp imp;
        imp.m_os = &os;
        return imp;
    }
    
    
    int main() 
    { 
        std::vector<char> v{'a', 'b'}; 
        std::cout << foo_vector_print() << v << std::endl; 
    }   
    

    【讨论】:

    • 我猜setw 更简单,因为它只接受对流的引用,调用它的方法,然后返回对同一对象的引用。上面的答案将不同的对象返回给不同的类“推送”原始流,然后下一个&lt;&lt;“弹出”原始流。
    • @einpoklum 你可以调用setw 并在两个不同的语句中输出你的东西。在那里你必须链接你的运营商。所以它略有不同,但在大多数情况下并不明显。
    • 基本上你刚刚将一个对print_in_fancy_way 的调用封装到一个操作员a 解决方案。
    • 不,它实际上一个解决方案。在修改器的管道之后,您不能管道除了矢量之外的其他东西;跟随向量的管道,修饰符的效果是相反的。
    【解决方案3】:

    嗯,您必须在某处编写自定义代码。您的矢量内容不会自己写入std::ostream

    从外观上看,您确实是在问,如何能够使用&lt;&lt; 运算符对std::vector&lt;char&gt; 进行操作。以下示例应该可以帮助您入门:

    #include <iostream>
    #include <vector>
    
    std::ostream &operator<<(std::ostream &o, const std::vector<char> &v)
    {
        // Fill in the blanks here.
        return o;
    }
    
    // Example:
    
    int main()
    {
        std::vector<char> v;
    
        std::cout << v;
    
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-09-18
      • 2013-07-22
      • 2016-04-05
      • 2018-05-31
      • 1970-01-01
      • 2023-03-17
      相关资源
      最近更新 更多