Несколько операторов вывода? - PullRequest
3 голосов
/ 05 октября 2009

можно ли определить несколько операторов вывода для перечисления? Я хочу использовать это

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

оператор (1) печатает текст, читаемый человеком, и (2) преобразует его в некоторый код для хранения в базе данных.

Спасибо

Ответы [ 5 ]

4 голосов
/ 05 октября 2009

Создайте оболочки, которые будут возвращать некоторый объект вместо ostream и которые будут обрабатывать печать. В вашем случае это будет объект для печати humand-readable значения и объект для печати кода базы данных. Вот грубый пример, который печатает удобочитаемую форму и целочисленную форму. Класс ostream_enum_wrapper_human с его оператором << используется для печати читабельной формы, класс <code>ostream_enum_wrapper_int с его << используется для печати целочисленного кода. Для переключения с ostream & на оболочку используется оператор << (ostream &, wrappertag), который оборачивает объект ostream внутри оболочки и возвращает завернутый объект. Поэтому следующий оператор << вызывается для объекта-оболочки, а не для ostream &, и класс-оболочка знает, как вывести значение. </p>

#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;
}

печатает ноль0два

2 голосов
/ 05 октября 2009

Вы можете воспользоваться перегрузкой по первому аргументу.

//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);  }
0 голосов
/ 05 октября 2009

Это решение далеко от совершенства; но в твоей проблеме нет ничего хорошего.

class MyClass {...};

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

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

Как видите, я помещаю потоковые операторы в пространства имен. В результате я должен включить пространство имен в текущее. Это просто делается с помощью простого объявления использования.

using namespace xml;

Хитрость заключается в том, чтобы поместить объявление using в наименьшую возможную область видимости.

0 голосов
/ 05 октября 2009

Предпочтительным способом является использование 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"
}
0 голосов
/ 05 октября 2009

Конечно, почему нет? Вам придется создать производный класс ostream, который реализует запись в базу данных, почти так же, как ofstream запись в файл. Дьявол кроется в деталях.

...