【问题标题】:Multiple output operators?多个输出运算符?
【发布时间】:2009-10-05 06:40:58
【问题描述】:

是否可以为一个枚举定义多个输出运算符?我想用这个

std::ostream& operator<< (std::ostream& os, my_enum e);

操作员 (1) 打印人类可读的文本并 (2) 将其转换为一些代码以存储在数据库中。

谢谢

【问题讨论】:

    标签: c++ operators operator-overloading enums


    【解决方案1】:

    创建将返回一些对象而不是处理打印的 ostream& 的包装器。在您的情况下,它将反对打印人类可读的值和打印数据库代码的对象。这是打印人类可读形式和整数形式的粗略示例。 ostream_enum_wrapper_human 类及其运算符 ostream_enum_wrapper_int 及其

    #include <iostream>
    using namespace std;
    class ostream_enum_wrapper_human
    {
        public:
        ostream& out;
        ostream_enum_wrapper_human(std::ostream& _out) : out(_out){}
    
    };
    
    class ostream_enum_wrapper_int
    {
        public:
        std::ostream& out;
        ostream_enum_wrapper_int(std::ostream& _out) : out(_out){}
    };
    
    
    enum T{zero,one,two};
    
    struct human_readable{} HumanReadable;
    ostream_enum_wrapper_human operator << (ostream& out, human_readable){
        return ostream_enum_wrapper_human(out);
    }
    
    struct print_int{} PrintInt;
    ostream_enum_wrapper_int operator << (ostream& out, print_int){
        return ostream_enum_wrapper_int(out);
    }
    
    
    ostream& operator << (ostream_enum_wrapper_human out, T t)
    {
        switch(t) {
            case zero: out.out << "zero"; break;
            case one: out.out << "one"; break;
            case two: out.out << "two"; break;
        }
    
        return out.out;
    }
    
    ostream& operator << (ostream_enum_wrapper_int out, T t)
    {
        return out.out << static_cast<int>(t);
    }
    
    int main()
    {
        cout << HumanReadable << zero << PrintInt << zero << HumanReadable << two;
    }
    

    prints zero0two

    【讨论】:

      【解决方案2】:

      您可以利用第一个参数的重载。

      //For human-readable output
      std::ostream& operator<< (std::ostream& os, my_enum e);
      
      //For database;    note the absence VVV of & sign here
      std::ostream& operator<< (databasefmt fmt, my_enum e)
      {
         std::ostream& os = fmt.stream;
         // Write data to os
         // ...
         return os;
      }
      
      struct databasefmt{
         std::ostream& stream;
         databasefmt(std::ostream & s) : stream(s) {};
      };
      

      然后编写将流转换为包装 databasefmt 类的流修饰符,以便该修改后的流的下一个输出将是您枚举的数据库输出。打印代码如下所示:

      output_stream << "DATA: "<< database_format << my_enum::Value << "END OF DATA" ;
      // Type:   std::ostream   |   databasefmt   |            std::ostream          |
      

      和这样的包装器:

      //Variable is needed to avoid confusing parentheses in output operators    
      struct databasefmt_converter_t {} database_format;
      // we can't return reference, so we just return fairly small instance of wrapper
      databasefmt operator<< (std::ostream& os, databasefmt_converter_t const&)
      {  return databasefmt(os);  }
      

      【讨论】:

      • 您正在返回对 operator
      • @dalle :添加示例。顺便说一句,“流修饰符”是一种常用技术,并且有几种可能的实现方式,其中一些与我的示例不同。
      • @dalle :啊,确实,对不起。因此,我们必须在任何地方删除引用。
      • 次要挑剔:双下划线是为编译器(及其库)保留的。我可以建议使用后缀 _t(如 databasefmt_convert_t)吗?
      【解决方案3】:

      当然,为什么不呢?您必须创建一个实现写入数据库的ostream 派生类,这与ofstream 写入文件非常相似。魔鬼在细节中。

      【讨论】:

        【解决方案4】:

        首选的方法是使用 std::ios_base::xalloc 然后使用 std::ios_base::iword:

        int getxalloc()
        {
           static const int ix = std::ios_base::xalloc();
           return ix;
        }
        
        enum my_enum
        {
           zero,
           one,
           two,
        };
        
        std::ostream& operator<<(std::ostream& os, my_enum e)
        {
           switch (os.iword(getxalloc())
           {
           default:
           case 0:
              os << (int)e;
              break;
           case 1:
              switch (e)
              {
              case zero:
                 os << "zero";
                 break;
              case one:
                 os << "one";
                 break;
              case two:
                 os << "two";
                 break;
              default:
                 os << "unknown";
                 break;
              }
              break;
           }
        
           return os;
        }
        
        int main()
        {
           my_enum e = one;
           std::cout.iword(getxalloc()) = 0;
           std::cout << e << "\n"; // will output "1"
           std::cout.iword(getxalloc()) = 1;
           std::cout << e << "\n"; // will output "one"
        }
        

        之后,您可以添加一些自己的精美操纵器,而不是直接使用 std::ios_base::iword。像这样:

        inline std::ios_base& format_my_enum_as_int(std::ios_base& ib)
        {
           ib.iword(getxalloc()) = 0;
           return ib;
        }
        
        inline std::ios_base& format_my_enum_as_string(std::ios_base& ib)
        {
           ib.iword(getxalloc()) = 1;
           return ib;
        }
        
        int main()
        {
           my_enum e = one;
           std::cout << format_my_enum_as_int << e << "\n"; // will output "1"
           std::cout << format_my_enum_as_string << e << "\n"; // will output "one"
        }
        

        【讨论】:

          【解决方案5】:

          这个解决方案远非完美;但是对于您的问题没有真正好的解决方案。

          class MyClass {...};
          
          namespace xml
          {
              std::ostream& operator << (std::ostream& os, MyClass& c);
          }
          
          namespace text
          {
              std::ostream& operator << (std::ostream& os, MyClass& c);
          }
          

          如您所见,我将流运算符放在命名空间中。因此,我必须将命名空间包含在当前命名空间中。这只需简单的 using 声明即可完成。

          using namespace xml;
          

          诀窍是将 using 声明放在尽可能小的范围内。

          【讨论】:

            猜你喜欢
            • 2022-01-08
            • 1970-01-01
            • 1970-01-01
            • 2018-04-14
            • 1970-01-01
            • 1970-01-01
            • 2021-07-01
            • 2016-01-09
            • 1970-01-01
            相关资源
            最近更新 更多