Нестандартная перегрузка оператора вывода << :-D
От: Аноним  
Дата: 30.10.09 09:51
Оценка:
Доброго времени суток!
Вначале введу в курс дела, а после исходника напишу вопрос что меня волнует.

Извращаюсь над консольным выводом. Нужно мне сделать цветной вывод, одновременно заменив старую, неудобную на мой взгляд локализацию русского языка методом использования wcout, на использовании cout и CharToOem()
(Неудобной потому что мне постоянно при работе char в wchar приходилось конвертить а в stl это много времени кушает)
И все бы вышло удачно если бы не столкнулся с непонятной ситуацией.

Одно из того что делает моя программа, это проверяет данные хранящиеся в XML файлах с которыми она собственно и работает.
В консоль пользователю она должна выводить подробные и удобочитаемые ошибки и предупреждения.

Я поделил это на такие категории как:
1) Критические ошибки
2) Ошибки
3) Предупреждения
4) Текст


Для каждой из категорий планируется использовать определенные цвета текста в консоли и возможно определенную разметку для удобства чтения.

Далее еще не доделанный до конца исходник, но он уже прекрасно справляется с частью своих обязанностей:

Console.h
#ifndef CONSOLE_H
#define CONSOLE_H

#include <string>
#include <iostream>
#include <windows.h>

using namespace std;

//==================================================================================
// Пространство имен console
//==================================================================================
namespace console
{


// Цвета для консоли
enum ConsoleColor
{
    Black         = 0,
    Blue          = 1,
    Green         = 2,
    Cyan          = 3,
    Red           = 4,
    Magenta       = 5,
    Brown         = 6,
    LightGray     = 7,
    DarkGray      = 8,
    LightBlue     = 9,
    LightGreen    = 10,
    LightCyan     = 11,
    LightRed      = 12,
    LightMagenta  = 13,
    Yellow        = 14,
    White         = 15
};


//  Смена цвета шрифта выводимого консолью
void SetConsoleColor(const ConsoleColor &text, const ConsoleColor &background);


/***********************************************************************************/
/*  Класс вывовода информации о критических ошибках
/***********************************************************************************/
class CriticalError
{
  public:
    friend CriticalError& operator<<(CriticalError &class_obj, const string &str)
    {
        char buf[512];
        CharToOem(str.c_str(), buf);
        SetConsoleColor(LightRed, White);
        cout << buf;
        SetConsoleColor(White, Black);
        return class_obj;
    };

    friend CriticalError& operator<<(CriticalError &class_obj, const char *str)
    {
        char buf[512];
        CharToOem(str, buf);
        SetConsoleColor(LightRed, White);
        cout << buf;
        SetConsoleColor(White, Black);
        return class_obj;
    };

    template <typename T> friend CriticalError& operator<<(CriticalError &class_obj, const T &obj)
    {
        SetConsoleColor(LightRed, White);
        cout << obj;
        SetConsoleColor(White, Black);
        return class_obj;        
    };
};


/***********************************************************************************/
/*  Класс вывовода информации о некритических ошибках
/***********************************************************************************/
class Error
{
  public:
    friend Error& operator<<(Error &class_obj, const string &str)
    {
        char buf[512];
        CharToOem(str.c_str(), buf);
        SetConsoleColor(LightRed, Black);
        cout << buf;
        SetConsoleColor(White, Black);
        return class_obj;
    };

    friend Error& operator<<(Error &class_obj, const char *str)
    {
        char buf[512];
        CharToOem(str, buf);
        SetConsoleColor(LightRed, Black);
        cout << buf;
        SetConsoleColor(White, Black);
        return class_obj;
    };

    template <typename T> friend Error& operator<<(Error &class_obj, const T &obj)
    {
        SetConsoleColor(LightRed, Black);
        cout << obj;
        SetConsoleColor(White, Black);
        return class_obj;        
    };
};


/***********************************************************************************/
/*  Класс вывовода информации о предупреждениях
/***********************************************************************************/
class Warning
{
  public:
    friend Warning& operator<<(Warning &class_obj, const string &str)
    {
        char buf[512];
        CharToOem(str.c_str(), buf);
        SetConsoleColor(Yellow, Black);
        cout << buf;
        SetConsoleColor(White, Black);
        return class_obj;
    };

    friend Warning& operator<<(Warning &class_obj, const char *str)
    {
        char buf[512];
        CharToOem(str, buf);
        SetConsoleColor(Yellow, Black);
        cout << buf;
        SetConsoleColor(White, Black);
        return class_obj;
    };

    template <typename T> friend Warning& operator<<(Warning &class_obj, const T &obj)
    {
        SetConsoleColor(Yellow, Black);
        cout << obj;
        SetConsoleColor(White, Black);
        return class_obj;        
    };
};


/***********************************************************************************/
/*  Класс вывовода стандартной информации и статистики
/***********************************************************************************/
class Text
{
  public:
    friend Text& operator<<(Text &class_obj, const string &str)
    {
        char buf[512];
        CharToOem(str.c_str(), buf);
        SetConsoleColor(LightCyan, Black);
        cout << buf;
        SetConsoleColor(White, Black);
        return class_obj;
    };

    friend Text& operator<<(Text &class_obj, const char *str)
    {
        char buf[512];
        CharToOem(str, buf);
        SetConsoleColor(LightCyan, Black);
        cout << buf;
        SetConsoleColor(White, Black);
        return class_obj;
    };

    template <typename T> friend Text& operator<<(Text &class_obj, const T &obj)
    {
        SetConsoleColor(LightCyan, Black);
        cout << obj;
        SetConsoleColor(White, Black);
        return class_obj;        
    };
};


/***********************************************************************************/
/*  Класс для вывода информации в консоль
/***********************************************************************************/
class Console
{
  public:
    Console() { SetConsoleColor(White, Black); }
    
    CriticalError  critical_error;
    Error          error;
    Warning        warning;
    Text           text;
};

// Внешняя переменная для работы с консолью
extern Console con;

} // end namespace

#endif



Console.cpp
#include "Console.h"  

//==================================================================================
// Пространство имен console
//==================================================================================
namespace console
{

// Внешняя переменная для работы с консолью
Console con;


/***********************************************************************************/
/*  Смена цвета шрифта выводимого консолью
/***********************************************************************************/
void SetConsoleColor(const ConsoleColor &text, const ConsoleColor &background)
{
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hStdOut, (WORD)((background << 4) | text));
}


} // end namespace




Данный модуль позволяет вот так возможно и не очень стандартно использовать его как замену cout

#include "Console.h"
#include <string>
using namespace console;
using namespace std;

int main(int argc, char* argv[])
{
  // это работает прекрасно
  con.text    << "Это простой текст " << 111 << "hghghg" << 55.99 << '\n';
  // и это работает замечательно
  con.warning << string("Это текст предупреждения!") << '\n';

  // А вот это нефига не хочет и жалуется на endl
  con.text << string("Это не хочет работать") << endl;
 << endl
}


Выдает оно вот такую ошибку:
d:\projects\....путь к папке....\main.cpp(139): error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'overloaded-function' (or there is no acceptable conversion)

Я прекрасно понимаю что что это означает но как исправить и перегрузить оператор << для endl понятия не имею.

1) В общем вопрос как исправить чтобы endl тоже работал. Я понимаю что непринципиально возможно и легко от него отказатся заменив на '\n' но всеже хотелось бы знать?

2) Хотелось бы услышать мнение коллег по поводу вообще такой реализации. Не криво ли все так вот делать как я делаю?
Re: Нестандартная перегрузка оператора вывода << :-D
От: Sergey Россия  
Дата: 30.10.09 10:11
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Я прекрасно понимаю что что это означает но как исправить и перегрузить оператор << для endl понятия не имею.


Дело в том что в стандартных потоках есть оператор

stream_type& operator<<(stream_type& ( *pfn)(stream_type&))
{
  (*pfn)(*this);
  return *this;
}


специально для вызова манипуляторов без параметров. Сделайте себе такой же. Соответственно, endl вызывает у потока сначала put, потом — flush.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[2]: Нестандартная перегрузка оператора вывода << :-D
От: tatsu  
Дата: 30.10.09 11:14
Оценка:
Здравствуйте, Sergey, Вы писали:

S>Здравствуйте, Аноним, Вы писали:


А>>Я прекрасно понимаю что что это означает но как исправить и перегрузить оператор << для endl понятия не имею.


S>Дело в том что в стандартных потоках есть оператор


S>
S>stream_type& operator<<(stream_type& ( *pfn)(stream_type&))
S>{
S>  (*pfn)(*this);
S>  return *this;
S>}
S>


S>специально для вызова манипуляторов без параметров. Сделайте себе такой же. Соответственно, endl вызывает у потока сначала put, потом — flush.


Чтото у меня не получается а смысл манипуляторов без параметров для меня пока загадочен
Я попытался сделать как вы сказали и добавил эту запись в сво классы
Я пробывал добавить и так


ostream& operator<<(ostream& (*pfn)(ostream&))
{
  (*pfn)(*this);
  return *this;
}



и так (для класса Text)


Text& operator<<(Text& (*pfn)(Text&))
{
    (*pfn)(*this);
    return *this;
}


В итоге на первое оно ругается
а на второе оно выводит ту же ошибку что и ранее

Вот теперь как выглядит мой класс Text

/***********************************************************************************/
/*  Класс вывовода стандартной информации и статистики
/***********************************************************************************/
class Text
{
  public:
    friend Text& operator<<(Text &class_obj, const string &str)
    {
        char buf[512];
        CharToOem(str.c_str(), buf);
        SetConsoleColor(LightCyan, Black);
        cout << buf;
        SetConsoleColor(White, Black);
        return class_obj;
    };

    friend Text& operator<<(Text &class_obj, const char *str)
    {
        char buf[512];
        CharToOem(str, buf);
        SetConsoleColor(LightCyan, Black);
        cout << buf;
        SetConsoleColor(White, Black);
        return class_obj;
    };

    Text& operator<<(Text& (*pfn)(Text&))
    {
        (*pfn)(*this);
        return *this;
    };

    template <typename T> friend Text& operator<<(Text &class_obj, const T &obj)
    {
        SetConsoleColor(LightCyan, Black);
        cout << obj;
        SetConsoleColor(White, Black);
        return class_obj;        
    };
};


Обьясните пожалуйста что я делаю не так
Re[3]: Нестандартная перегрузка оператора вывода << :-D
От: Sergey Россия  
Дата: 30.10.09 11:45
Оценка: 3 (1)
Здравствуйте, tatsu, Вы писали:

T>Чтото у меня не получается а смысл манипуляторов без параметров для меня пока загадочен


endl, flush или например hex — манипуляторы без параметра. Представляют из себя функции с 1 аргументом.
Можно как функцию:
 endl(s);

А можно так:
 s << endl;


Для того чтобы была допустима вторая форма вызова, нужен ostream& operator<<(ostream& (*pfn)(ostream&))
Который внутри себя вызывает переданную ему функцию.

T>Я попытался сделать как вы сказали и добавил эту запись в сво классы

T>Я пробывал добавить и так

T>
T>ostream& operator<<(ostream& (*pfn)(ostream&))
T>{
T>  (*pfn)(*this);
T>  return *this;
T>}
T>


У вас не стрим, этот вариант не нужен.

T>и так (для класса Text)



T>
T>Text& operator<<(Text& (*pfn)(Text&))
T>{
T>    (*pfn)(*this);
T>    return *this;
T>}
T>


T>В итоге на первое оно ругается

T>а на второе оно выводит ту же ошибку что и ранее

По-моему — наоборот — первый вариант ничего не меняет, во втором — не скомпилируется вызов (*pfn)(*this);

T>Обьясните пожалуйста что я делаю не так


endl тоже свой нужен, стандартный работает только со стримами.
Нужен примерно такой:
Text& endl(Text& o)
{
   o << "\n";
   o.flush();  // или cout.flush
}

Ну то есть сначала добейтесь, чтобы у вас компилировался вызов endl(con.text), а потом уже приделывайте оператор <<
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[4]: Нестандартная перегрузка оператора вывода << :-D
От: tatsu  
Дата: 30.10.09 12:32
Оценка:
Ну наконецто получилось
Спасибо огромное!

Пока еще не все понимаю но уже чтото поясняется. Слишком запутано все оказалось.

Еще не совсе понял предназначение flush так как все время както с ним не сталкивался.
Обычно потоки както сами все выталкиваю туда куда нужно и с этим проблем не было)

А endl пришлось вынести в .cpp модуля и написать прототип в .h
чтобы endl был доступен напрямую а не через con.text.endl как получилось у меня сначала

Пока сделал так:
Text& endl(Text& class_obj)
{
    cout << '\n';
    cout.flush();
    return class_obj;
}


Всеже гдето внутри терзает меня что это может выглядеть далеко непрофессионально и кривенько, но раз уж вы молчите то видимо реализация терпимая

Теперь буду пытатся правильно наследовать и виртуальные функции писать чтобы и код ужать и до ума доделать.

Еще раз спасибо!
Re: Нестандартная перегрузка оператора вывода << :-D
От: TimurSPB Интернет  
Дата: 30.10.09 12:59
Оценка:
....

friend CriticalError& operator<<(CriticalError &class_obj, const string &str)
{
char buf[512];

....

А почему 512?
Make flame.politics Great Again!
Re[5]: Нестандартная перегрузка оператора вывода << :-D
От: Sergey Россия  
Дата: 30.10.09 13:19
Оценка:
Здравствуйте, tatsu, Вы писали:

T>Всеже гдето внутри терзает меня что это может выглядеть далеко непрофессионально и кривенько, но раз уж вы молчите то видимо реализация терпимая


Ну на вкус и на цвет все карандаши разные, мало ли кому чего удобнее. Для себя я бы скорее всего сделал переключение режимов манипуляторами, чтобы пользоваться можно было так:

log << warning(4) << "это пишем желтым" << 500 << "все до следующего манипулятора желтым выводится" << text(30) << "теперь обычным белым" << error << "это красеньким"


А цифирка у warning и text — уровень логгирования, при котором соответствующие сообщения выводятся. Но это, повторюсь, кому какой интерфейс больше нравится.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[2]: Нестандартная перегрузка оператора вывода << :-D
От: tatsu  
Дата: 30.10.09 21:54
Оценка:
Здравствуйте, TimurSPB, Вы писали:

TSP>....

TSP>

TSP> friend CriticalError& operator<<(CriticalError &class_obj, const string &str)
TSP> {
TSP> char buf[512];

TSP>....

TSP>А почему 512?


Незнаю
Так захотелось 256 бывает мало. Очень редко но бывает)
А сколько нужно?
Re[3]: Нестандартная перегрузка оператора вывода << :-D
От: TimurSPB Интернет  
Дата: 31.10.09 00:12
Оценка:
TSP>>А почему 512?

T>Незнаю

T>Так захотелось 256 бывает мало. Очень редко но бывает)
T>А сколько нужно?

Это один из вопросов верхнего мира. Тайна размера буфера в операциях со строками есть великая тайна.
Make flame.politics Great Again!
Re: Нестандартная перегрузка оператора вывода << :-D
От: Кодт Россия  
Дата: 31.10.09 00:56
Оценка: 3 (1)
1) Почему бы сразу не писать исходники в OEM-кодировке? Тогда пропадёт необходимость перекодировать на лету.
2) Расточительно написан код (копипаст работы с цветом). Я бы переделал...
class color_out
{
  ostream& ost_;
  unsigned color_;
  color_out(ostream& ost, unsigned c) : ost_(ost), color_(c) {}

  template<class T>
  const color_out& operator << (const T& rhs) const // спокойно можем работать с константами :)
  {
    output_colored(rhs);
    return *this;
  }
  const color_out& operator << (ostream&(manip*)(ostream&)) const // уточняем, с какой сигнатурой манипуляторов имеем дело
  {
    output_colored(manip);
    return *this;
  }

  // единый способ обрамить цветом
  template<class T>
  void output_colored(const T& rhs) const
  {
    unsigned const c = GetColor(); SetColor(color_); // преврати в SetConsoleColor сам
    output(rhs);
    SetColor(c);
    return *this;
  }

  // обрабатываем строки и всё остальное
  template<class T>
  void output(const T& v) const
  {
    ost_ << v;
  }
  void output(const string& v) const
  {
    output_oem(v.c_str());
  }
  void output(const char* v) const
  {
    output_oem(v);
  }
  void output(char c) const
  {
    const char s[2] = { c, 0 };
    output_oem(s);
  }

  // все строки - конвертируем одним и тем же способом
  void output_oem(const char* s) const
  {
    vector<char> buf(strlen(s)+1); // мультистринг не поддерживаем, ибо нафиг
    CharToOem(s, &buf.front());
    ost_ << &buf.front();
  }
};

template<ostream& Ost, unsigned Color>
struct the_color_out : color_out
{
  the_color_out() : color_out(Ost, Color) {}
};

typedef the_color_out<cerr, RED_ON_BLUE> critical;
typedef the_color_out<cout, RED_ON_BLACK> error;
typedef the_color_out<cout, YELLOW_ON_BLACK> warning;
typedef the_color_out<cout, GREEN_ON_BLACK> success;
// можно и глобальными, и статическими константами сделать - на вкус и цвет.

.....
critical() << "aaaaa!!!" << endl;


А что касается — почему у тебя ошибка компиляции была, так это просто.
Манипуляторы, такие как endl, имеют несколько сигнатур: ostream&(ostream&) и wostream&(wostream&).
Когда ты вызываешь функцию f(endl), компилятор пытается определить, какая из сигнатур тебе нужна.
А ты предлагаешь выбор из string, const char* и шаблонного T. Первые два не подходят, последний сохраняет неопределённость.

Кстати, если у тебя есть типы, которые умеют выводить себя в ostream, и при этом собираются вывести русские строки (ну например, ты определил ostream& operator<<(ostream&, const vector<string>&) и т.п.), то конвертирование на уровне обёртки над потоком уже не спасёт. Потому что внутри этого оператора про обёртку уже ничего не известно.
Следовательно, нужно вламываться внутрь ostream'а; это можно сделать двумя способами:
— унаследоваться от ostream и переопределить его виртуальные функции
написать собственный streambuf и назначить его ostream'у (т.е. прямо cout'у)
Последнее — идеологически наиболее правильно, хотя и трудоёмко.
Перекуём баги на фичи!
Re: Нестандартная перегрузка оператора вывода << :-D
От: TimurSPB Интернет  
Дата: 31.10.09 01:12
Оценка:
Складывается ощущение, что воспроизводится log4cplus, только разноцветный.
Make flame.politics Great Again!
Re[4]: Нестандартная перегрузка оператора вывода << :-D
От: tatsu  
Дата: 31.10.09 09:28
Оценка:
Здравствуйте, TimurSPB, Вы писали:

TSP>>>А почему 512?


T>>Незнаю

T>>Так захотелось 256 бывает мало. Очень редко но бывает)
T>>А сколько нужно?

TSP>Это один из вопросов верхнего мира. Тайна размера буфера в операциях со строками есть великая тайна.


В том то и дело что до сих пор существует тьма программ так до сих пор не перешедщих на STL изза глючености.
Верю что есть еще решения этой задачи но увы в нашем мире все зависит от размера буферов

Был както случай когда еще на старом добром Си я с одним товарищем немного программил.
Требовалось написать функцию которая выдирает из строки формата "par1|par2|par3..."
выдирает указанный числом параметр. Так вот товарищь тот реализовал так что максимальное для параметра было 60 символов но мого регулироватся. Памяти жрало (хоть счас памяти гигабайтами однако плохой код от этого лучше работать не начет).
В общем я потом переписал функцию так чтобы работала без ограничений. В итоге не знаю как по скорости но уж по размеру исходного кода сразу все становилось понятно. вот
Re[2]: Нестандартная перегрузка оператора вывода << :-D
От: tatsu  
Дата: 31.10.09 09:35
Оценка:
Спасибо за предложенные варианты. Счас буду все нализировать и разбирать, и учится новому ^___^
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.