Потомок std::ofstream, не работает манипулятор

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Код: Выделить всё

typedef std: :o stream &(*TManipulator)(std: :o stream  &Stream);
class TMyStream : public std: :o fstream
{
 public:
 ...
  TMyStream &operator << (const TManipulator Manipulator)
  {
   Manipulator(*((std: :o fstream*)this));
   return *this;
  }
 ...
};
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Если честно, я на эту тему в своё время не обратил внимания, просто по верхам глянул. Сейчас посмотрел внимательно на код и удивился, почему ты ожидал, что он будет компилиться, ведь у тебя операторы перегружены только для указателей на символы, а для манипулятора - нет.

А решение вообще убило. Определить оператор для функции, принимающей стандартный стрим. А зачем так изголяться, расскажи? Может я чего-то не понимаю? Что мешает определить оператор, принимающий стрим, который возвращает манипулятор? Ты понимаешь, что твоё текущее решение запрещает клеить операторы << после манипулятора, не говоря уже о том, что это костыль-костыльный сам по себе? Это называется "как сделать очевидную вещь не очевидной".

И этот человека рассуждает о теории множеств и о том, что студенческий оператора "минус" нарушает семантику операции... а у тебя здесь семантика ни разу не нарушена, как ты думаешь? :)

Уж лучше бы это было спорное нарушение, как у студента, чем вот такой откровенный костыль.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Romeo писал(а):Сейчас посмотрел внимательно на код и удивился, почему ты ожидал, что он будет компилиться, ведь у тебя операторы перегружены только для указателей на символы, а для манипулятора - нет.
И? А почему он не наследуется?
Romeo писал(а):А решение вообще убило. Определить оператор для функции, принимающей стандартный стрим. А зачем так изголяться, расскажи? Может я чего-то не понимаю? Что мешает определить оператор, принимающий стрим, который возвращает манипулятор?
А зачем мне писать свои манипуляторы? Они всё равно ничего нового не делают.
Romeo писал(а):Ты понимаешь, что твоё текущее решение запрещает клеить операторы << после манипулятора, не говоря уже о том, что это костыль-костыльный сам по себе? Это называется "как сделать очевидную вещь не очевидной".
Это с какого? Оператор принимает и возвращает мой класс. Если Вы не в состоянии даже отличить синтаксис перегрузки от завёрнутого в тело применения функции над предком, то какого берётесь оценивать чьи то знания? Тест, кстати, тоже говорит от обратном.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Сионист писал(а):И? А почему он не наследуется?
Потому, что так сказал дедушка Страуструп. Это закреплено стандартом, который ты никак не хочешь прочесть.
Сионист писал(а):А зачем мне писать свои манипуляторы? Они всё равно ничего нового не делают.
А где я написал, что нужно написать свой манипулятор? Прочти ещё раз моё сообщение. Там было написано об операторе, принимающем стрим (!), который возвращает манипулятор.
Сионист писал(а):Это с какого? Оператор принимает и возвращает мой класс. Если Вы не в состоянии даже отличить синтаксис перегрузки от завёрнутого в тело применения функции над предком, то какого берётесь оценивать чьи то знания? Тест, кстати, тоже говорит от обратном..
Мне кажется именно ты не разбираешься в синтаксисе. Можешь мне объяснить, что обозначает вот это объявление?

Код: Выделить всё

typedef std: :o stream &(*TManipulator)(std: :o stream  &Stream);

Давай я расшифрую, так как у тебя с этим проблемы, судя по последнему сообщению. Тут объявляется тип. Этот тип является указателем на функцию, которая принимает ссылку на стандратный стрим и возвращает ссылку на стандратный стрим. Далее смотрим сюда:

Код: Выделить всё

TMyStream &operator << (const TManipulator Manipulator)
Это оператор, который принимает указатель на функцию (а совсем не твой класс, как ты пишешь). Не вдаваясь даже в то, что const тут бесполезен, перейдём к вызову этого оператора.

Код: Выделить всё

TMyStream stream;
stream << std::endl;
Фактически в этом месте MinGW подставляет тебе перед std::endl оператор взятия адреса, то есть код выглядит вот так:

Код: Выделить всё

TMyStream stream;
stream << &std::endl;
Как следствие, оператор получает манипулятор, как указатель на функцию (коим он, по сути, и является) и потом, внутри себя вызывает эту функцию:

Код: Выделить всё

Manipulator(*((std: :o fstream*)this));
Это работает лишь потому, что у тебя MinGW. Не все компиляторы подставляют автоматически амперсанд в том месте, где нужен указатель на функцию, об этом мы с тобой разговаривали пару месяцев назад в другой теме.

Что ты теряешь? При таком подходе ты не можешь приклеить к манипулятору справа через << выражения, как это все привыкли делать. Не говоря уже о том, что это просто ужасное решение с архитектурной точки зрения.

А теперь внимательно прочти мой предыдущий пост ещё раз, и ты сможешь понять, как нужно поменять код, чтобы всё было хорошо.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Romeo писал(а):Потому, что так сказал дедушка Страуструп. Это закреплено стандартом, который ты никак не хочешь прочесть.
Именно там и сказано, что члены предка наследуются. И ни какого амбигуса при именно таком наследовании на поверхности не валялось.
Мне кажется именно ты не разбираешься в синтаксисе. Можешь мне объяснить, что обозначает вот это объявление?
Код cpp:
typedef std: :o stream &(*TManipulator)(std: :o stream &Stream);
Всего лишь тип указателя на функцию, принимающую ссылку на std: :o stream. А
Romeo писал(а): Давай я расшифрую, так как у тебя с этим проблемы, судя по последнему сообщению. Тут объявляется тип. Этот тип является указателем на функцию, которая принимает ссылку на стандратный стрим и возвращает ссылку на стандратный стрим. Далее смотрим сюда:

Код: Выделить всё

TMyStream &operator << (const TManipulator Manipulator)
Это оператор, который принимает указатель на функцию (а совсем не твой класс, как ты пишешь)
Не статические функции-члены и статические операторы-члены имеют ещё один не явный параметр/операнд - экземпляр своего класса, передаваемый по указателю this, от него они и вызываются. В случае не статического оператора-члена этот неявный операнд левый, в случае не статической функции-члена предшествует оператору точка, либо располагается по адресу в указателе, предшествующем оператору минус равно. Так что мой класс оператор тоже принимает. Слева.
Что ты теряешь? При таком подходе ты не можешь приклеить к манипулятору справа через << выражения, как это все привыкли делать. Не говоря уже о том, что это просто ужасное решение с архитектурной точки зрения.
Могу. И даже уже приклеил, дальше даже поставил

Код: Выделить всё

<<std::endl<<std::endl
и ещё раз приклеил оператор <<. Обратите внимание: std: :o fsream принимает и возвращает функция, указатель на которую принят в правом операнде, а сам оператор принимает слева и возвращает мой класс. Так как остальные версии << также возвращают мой класс, то к какой бы из них кто ни прилепил справа

Код: Выделить всё

<<std::ednl
, левый операнд будет экземпляром моего класса и что бы ни кто бы ни прилепил справа к

Код: Выделить всё

<<std::ednl
, левым операндом опять будет экземпляр моего класса. Предок завёрнут только в тело.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Сионист писал(а):Именно там и сказано, что члены предка наследуются. И ни какого амбигуса при именно таком наследовании на поверхности не валялось.
А ничего, что все эти операторы не принадлежат базовому классу, а являются внешними функциями? О каком наследовании может идти речь?
Сионист писал(а): Не статические функции-члены и статические операторы-члены имеют ещё один не явный параметр/операнд - экземпляр своего класса, передаваемый по указателю this, от него они и вызываются. В случае не статического оператора-члена этот неявный операнд левый, в случае не статической функции-члена предшествует оператору точка, либо располагается по адресу в указателе, предшествующем оператору минус равно. Так что мой класс оператор тоже принимает. Слева.
Вот зачем ты это всё написал? Ты бы ещё таблицу умножения сюда втулил, капитан. Я отлично знаю о this, но когда я кому-то говорю, что мой метод что-то принимает, то я всегда имею в виду его ЯВНЫЙ параметр. Ожидал от тебя такой же конструктивной позиции. Вот именно поэтому с тобой очень сложно общаться. Очевидные вещи ты считаешь непонятными, а не очевидные наоборот считаешь очевидными.

Люди, кто-то ещё мог предложить по предыдущему посту человека, что он имел в виду this?
Сионист писал(а): Могу. И даже уже приклеил, дальше даже поставил

Код: Выделить всё

<<std::endl<<std::endl
и ещё раз приклеил оператор <<. Обратите внимание: std: :o fsream принимает и возвращает функция, указатель на которую принят в правом операнде, а сам оператор принимает слева и возвращает мой класс. Так как остальные версии << также возвращают мой класс, то к какой бы из них кто ни прилепил справа

Код: Выделить всё

<<std::ednl
, левый операнд будет экземпляром моего класса и что бы ни кто бы ни прилепил справа к

Код: Выделить всё

<<std::ednl
, левым операндом опять будет экземпляр моего класса. Предок завёрнут только в тело.
Да, тут я не довертел в голове. Действительно, возможность клеить выражения справа не теряется. Но аргумент о том, что это страшный костыль никак не устраняется, к сожалению. Ты разрываешь цепочку операторов самым неожиданным способом (приняв манипулятор, как указатель на функцию). Неожиданность - это одна из самых плохих вещей в проектировании системы. Программист всегда должен действовать по принципу наименьшего удивления, тогда его код будет максимально просто поддерживать.

Чуть позже я предоставлю пример, когда из-за такого кривого подхода у тебя всё станет работать не так, как ожидалось, просто сразу в голове не приходит, а времени уже нет. Отвечу чуть позже вечером.

И по-прежнему призываю всё привести к однородной архитектуре, которая принята стандартом для операторов <<, тогда костыли пропадут сами по себе.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

А ничего, что все эти операторы не принадлежат базовому классу, а являются внешними функциями? О каком наследовании может идти речь?
Функция функцией. Но как она вызывается?

Код: Выделить всё

std: :o fstream f;
...
std::endl(f);
? Нет, обычно не так.

Код: Выделить всё

std: :o fstream f;
...
f<<std::endl;
, а << - оператор-член. И компилятор ругался не на то, что оператора << нет вообще, а на амбигус.
Ты разрываешь цепочку операторов самым неожиданным способом (приняв манипулятор, как указатель на функцию).
А как же он принимается в std: :o fstream? Разве не так?
Ты бы ещё таблицу умножения сюда втулил, капитан. Я отлично знаю о this, но когда я кому-то говорю, что мой метод что-то принимает, то я всегда имею в виду его ЯВНЫЙ параметр. Ожидал от тебя такой же конструктивной позиции. Вот именно поэтому с тобой очень сложно общаться. Очевидные вещи ты считаешь непонятными, а неочевидные наоборот считаешь очевидными. Люди, кто-то ещё мог предложить по предыдущему посту человека, что он имел в виду this?
А ничего, что неявен он лишь в прототипе и теле оператора и вполне явен синтаксисе вызова? Да и словосочетания "явно принимает" не было.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Ну ты бы хоть открыл бы для приличия стандартный хедер и посмотрел, как там выглядит этот оператор. Даже если ты не знаешь, что все потоковые операторы являются внешними (хотя это нужно знать), то открыть код STL и посмотреть, как оно на самом деле - это самое простое, что можно сделать в такой ситуации.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

Ну там, к слову сказать, не понимаю кому нужен от потока ввода-вывода какой-то функционал кроме как прочитать 1..N байт/записать N байт. Проверить eof. ioctl() для портов. Ну и для блочных устройств - seek/tell.

iostream же сионист какой-то писал. Бриллиантовое наследование от std::ios зачем-то вкрутили. Потоки ввода-вывода вправо-влево сдвигают. Спроси любого программиста: каков полный интерфейс std::iostream? Если он ответит правильно, то его надо увольнять: он не интересуется теми вещами которые имеют практическую пользу, т.е фронтендом, базами данных, анализом данных, 2D/3D графикой, численным моделированием, сетевыми протоколами итд итп. Он изучает вещи в себе.

Человек и библиотека нашли друг друга.
2B OR NOT(2B) = FF
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Romeo писал(а):Ну ты бы хоть открыл бы для приличия стандартный хедер
Не открывается.
Absurd писал(а):Ну там, к слову сказать, не понимаю кому нужен от потока ввода-вывода какой-то функционал кроме как прочитать 1..N байт/записать N байт. Проверить eof. ioctl() для портов. Ну и для блочных устройств - seek/tell.
Как раз этот функционал к потоку вообще не относится. Наоборот, при выводе в поток, например, целого оператор выводит строчную запись числа, а не байты самого данного. А конкретно этот класс ещё перекодирует выводимые строки из utf-32 в utf-8, умеет выводить PAINTSTUCT, HDC и RECT. При выводе данного любого их этих типов выводятся все его данные-члены, каждый обрамляется своими xml-тегами (открывающим и закрывающим) и они разделяются переводом каретки в начало следующей строки. А сырой вывод байтов - это WiteFile.
Absurd писал(а):Спроси любого программиста: каков полный интерфейс std::iostream? Если он ответит правильно, то его надо увольнять: он не интересуется теми вещами которые имеют практическую пользу, т.е фронтендом, базами данных, анализом данных, 2D/3D графикой, численным моделированием, сетевыми протоколами итд итп. Он изучает вещи в себе.
Тогда откуда ему знать интерфейс std::iosream и даже вообще знать об его существовании?
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Ответить