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

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

Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

Как раз этот функционал к потоку вообще не относится.
Поток - это объект операционной системы. Ну там завернутый в какую-то обертку языка и стандартной библиотеки. Он работает с байтами. Для распознавания кодировок и конверсии байтов в стринги должны применяться кодеки. Парсинг текста это вообще достаточно нетривиальная операция. CSV, XML, YAML или Json можно прочитать библиотекой, для прочих кейсов в общем случае ANTLR или Flex/Bison. Говорят что boost::spirit вполне годен, но я не любитель сложных шаблонов. То есть чтобы прочитать сложный файл нужно собрать композицию из объектов Parser(Lexer(Codec(raw_input_stream))). Это нормальное техническое решение, напоминающее конвейер по принципу работы. Ну или цепочку "сопрограмм", как их называет Дональд Кнут.
2B OR NOT(2B) = FF
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Он не работает с байтами. Это объект. И работает он со значениями без отрыва от интерпретации байтов. Именно поэтому не надо отдельно заботиться о переводе числа из внутреннего представления в строковое, за это отвечает поток. Если внутреннее представление какого то типа совпадает с форматом файла, или экранного буфера, тогда именно для него высокоуровневый потоковый вывод есть вывод сырых байтов. В остальных случаях нет. Поток вывода берёт на себя кодирование в целевой формат, а поток вывода - раскодирование из имеющегося формата. Иначе это не поток ввода-вывода, не путайте с протоколом TCP, где термин "поток" имеет иное значение и означает лишь прозрачность границ пакета.
То есть чтобы прочитать сложный файл нужно собрать композицию из объектов Parser(Lexer(Codec(raw_input_stream)))
Это при парсинге связного текста, при вводе отдельных значений известных типов в ожидаемом порядке парсинг берёт на себя поток, а при выводе задача парсинга вообще не стоит.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

Сионист писал(а):Он не работает с байтами. Это объект. И работает он со значениями без отрыва от интерпретации байтов. Именно поэтому не надо отдельно заботиться о переводе числа из внутреннего представления в строковое, за это отвечает поток.
Один класс должен делать одно дело и делать его хорошо. Кроме того, он должен иметь минимальный интерфейс. Сложный функционал должен быть вынесен наружу. Должно быть легко строить произвольные композиции из разных объектов для того чтобы справляться с комбинаторным ростом сложности проекта. iostream же делает две или три вещи, скрывая детали под капотом. Т.е. он скрывает в себе кодек, лексер и даже пытается делать парсинг через перегрузки операторов << и >> для сложных типов. Интерфейс у него сложный, громоздкий и начисто лишен всякого изящества. Расширять его сложно. Попробуй вот ostream обернуть тем же lzw кодированием - там черт ногу сломает. Лично я не сунусь туда никогда. Хотя для FILE* это сделать тривиально, засунуть там несколько коллбэков в Си-структуру. Многие реализации это позволяют.
а при выводе задача парсинга вообще не стоит.
Стоит задача форматирования.
2B OR NOT(2B) = FF
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Один класс должен делать одно дело и делать его хорошо. Кроме того, он должен иметь минимальный интерфейс.
Эйси. Тогда на него нельзя сверху навешать ни какой класс. В итоге программу в целом ООП не упростит, а усложнит. Интерфейс класса должен быть оптимален и вытекать из назначения, а минимальный - это к протоколам первого уровня.
Стоит задача форматирования.
Для отдельных значений она столь же проста, не путайте свалку записей с осмысленным связным текстом, абстракция потока ввода-вывода лишь на один уровень выше функций ReadFile и WriteFile и ни какой сложный парсинг на этом уровне не предусмотрен, как и сложное форматирование.
Должно быть легко строить произвольные композиции из разных объектов для того чтобы справляться с комбинаторным ростом сложности проекта.
Для сокращения комбинаторной проекта в целом надо сокращать количество связей и участвующих в них сущностей, а не упрощать составляющие проекта. А по-Вашему простейшим языком программирования является язык ассемблера. На самом же деле он уступает в сложности лишь программированию в опкодах.
iostream же делает две или три вещи, скрывая детали под капотом.
Как минимум четыре. Но разве это один класс?
Интерфейс у него сложный, громоздкий и начисто лишен всякого изящества.
Куда уж проще.
Попробуй вот ostream обернуть тем же lzw кодированием - там черт ногу сломает. Лично я не сунусь туда никогда.
Правильно. Потому что этого не надо делать.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

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

Что такое "Навешивать сверху"? Я знаю отношения наследования, агрегирования и использования.
Интерфейс у него сложный, громоздкий и начисто лишен всякого изящества.
Куда уж проще.
Этот тред создал ты, я не я. У меня-то никаких проблем с iostream нет, я использую только два метода - read и write. Использовал бы Win32, POSIX или C stdio, но общий знаменатель для всех библиотек с которыми я в данный момент работаю это iostream.
Для сокращения комбинаторной проекта в целом надо сокращать количество связей и участвующих в них сущностей, а не упрощать составляющие проекта.
Ну вообще-то в рамках общепринятой современной парадигмы принято считать что для этого нужно предпочитать слабые зависимости сильным. В частности, по возможности применять использование вместо агрегирования и агрегирование вместо наследования. Парсер, лексер, кодек, враппер вокруг сисколла в одном флаконе это перпендикулярный подход общепринятой современной парадигме.
А по-Вашему простейшим языком программирования является язык ассемблера.
Хорошие абстракции упрощают а не усложняют работу. Проблема только в том что в iostream абстракции плохие.
Попробуй вот ostream обернуть тем же lzw кодированием - там черт ногу сломает. Лично я не сунусь туда никогда.
Правильно. Потому что этого не надо делать.
А если мне нужно поток байт сжимать на лету? lzw - потоковый алгоритм, он может это делать. Зачем вообще все это ООП нужно тогда?
2B OR NOT(2B) = FF
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Сионист писал(а):Не открывается.
Скажу прямо, пробовал хреново. Какой же ты программист, если не можешь найти в папке компилятора место, где расположен системный хедер и открыть его? Хорошо, я подскажу тебе, в MinGW стандартные С++ хедеры действительно упрятаны в нелогичное место, например у меня они лежат вот тут:
\MinGW\lib\gcc\mingw32\4.8.1\include\c++\
Но их нестандартное расположение не отменяет необходимость наличия у программиста (да что там, даже у обычного пользователя) умения производить поиск файлов на жёстком диске.

И так, теперь мы открываем файл ostream и смотрим, как объявлен оператор вывода для сишных строк:

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

  template<typename _CharT, typename _Traits>
    inline basic_ostream<_CharT, _Traits>&
    operator<<(basic_ostream<_CharT, _Traits>& __out, const _CharT* __s)
Там рядом есть ещё группа внешних операторов для других типов.

Для std::string ищем оператор в хедере, где объявлен сам класс std::string. Там он тоже внешний.

Думаю, продолжать не стоит.

А теперь вернёмся к нашей изначальной проблеме. Как заставить работать указанный код? Достаточно сделать твои операторы внешними. И всех делов-то.

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

#include <iostream>
#include <fstream>

class TMyStream : public std::ofstream { };

TMyStream& operator << (TMyStream& os, const char* String)
{
	(std::ofstream&)os << "Specific narrow string operator: " << String;
	return os;
}

TMyStream& operator << (TMyStream& os, const wchar_t* String)
{
	(std::ofstream&)os << "Specific wide string operator: " << String;
	return os;
}

int main()
{
	TMyStream stream;
	stream.open("D:\\Temp\\temp.txt");

	stream << "Test" << std::endl;
	stream << L"Test" << std::endl;

	return 0;
}
Обрати внимание на конструкцию (std::ofstream&)os. Такое приведение обязательно для тестового примера. Если его уберёшь, то оператор начнёт в цикле вызывать себя бесконечное количество раз.

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

Absurd писал(а):Что такое "Навешивать сверху"? Я знаю отношения наследования, агрегирования и использования.
Ну это собирательная обзывалка для всего перечисленного. Разумеется не официальная и термином не являющаяся.
Romeo писал(а):Скажу прямо, пробовал хреново.
Вы можете лучше нажать правую кнопку и выбрать из всплывающего меню пункт open #include <fsream>? Однако!
Romeo писал(а):Как видишь, манипуляторы прекрасно работают и никакие костыли с оператором, принимающим указатель на функцию, не нужны.
Пока потомок пуст, и у меня всё работает вообще без перегрузки. Но при наличии в потомке хотябы одного оператора пришлось выкручиваться. И функция

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

TMyStream& endl << (TMyStream &Stream)
{
 *((std: :o fstream*)(&Stream))<<std::endl;
  return Stream;
}
так:

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

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

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

Так с вынесенными вообще приходится возвращать ссылку на std: :o stream и принимать его же, иначе оператор << не принимает цепную форму.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Ответить