Доброго времени суток, all.
Есть задача писать в поток древовидную структуру данных, каждый из узлов дерева представляет собой либо целое число, либо вещественное число, либо строку символов.
Проблема №1
Каждый из типов данных должен быть специально отформатирован. Для вещественного и целого типов форматирование можно порулить модификаторами dec, scientific, showpoint, setprecision и им подобным.
Все строки должны быть взяты в кавычки. Хотелось бы получить такой функционал посредством создания своего модификатора, что-то в роде:
Проблема №2
Т.к. выводить нужно древовидную структуру, то хотелось бы отбивать уровень дерева tab-ами. Опять же используя модификатор. setw и setfill не подходят, т.к заранее неизвестно, сколько символов будет в текстовом представлении выводимой переменной.
Первое решение, которое пришло в голову -- унаследоваться от std::ostream, добавить методы settablevel(int) и перегрузить ostream& operator<<(ostream&, const & std::string). Но как потом заставить это работать и с fstream и со stringstream, ведь они наследуют ostream, но ни как не мой самопальный класс.
Следующее, что подумалось -- boost::iostreams. Ранее с boost-ом сталкиваться не приходилось, и беглый осмотр iostreams не дал результатов.
Как решить проблемы-то? Уж больно не хочется руками форматировать...
Заранее спасибо.
Re: Наследование std::ostream и форматирование вывода
Здравствуйте, Madkinder, Вы писали:
M>Доброго времени суток, all. M>Есть задача писать в поток древовидную структуру данных, каждый из узлов дерева представляет собой либо целое число, либо вещественное число, либо строку символов.
M>Проблема №1 M>Каждый из типов данных должен быть специально отформатирован. Для вещественного и целого типов форматирование можно порулить модификаторами dec, scientific, showpoint, setprecision и им подобным.
M>Все строки должны быть взяты в кавычки. Хотелось бы получить такой функционал посредством создания своего модификатора, что-то в роде:
Я в таких случаях обычно завожу вспомогательный тип — например
Функция, как не трудно догадаться, нужна только для того чтобы не нужно было писать тип, которым инстанцируется quoted_t. Если закавычивать надо только строки, то шаблоны и функция она не нужны.
M>Проблема №2 M>Т.к. выводить нужно древовидную структуру, то хотелось бы отбивать уровень дерева tab-ами. Опять же используя модификатор. setw и setfill не подходят, т.к заранее неизвестно, сколько символов будет в текстовом представлении выводимой переменной.
M>Первое решение, которое пришло в голову -- унаследоваться от std::ostream, добавить методы settablevel(int) и перегрузить ostream& operator<<(ostream&, const & std::string). Но как потом заставить это работать и с fstream и со stringstream, ведь они наследуют ostream, но ни как не мой самопальный класс.
Тут вопрос — какой уровень автоматизма требуется. Если устраивает что-то вроде
Здравствуйте, Sergey, Вы писали:
M>>Проблема №1 M>>Все строки должны быть взяты в кавычки. Хотелось бы получить такой функционал посредством создания своего модификатора, что-то в роде:
S>Я в таких случаях обычно завожу вспомогательный тип для которого переопределяю оператор вывода, который и ставит кавычки, эскейпит запрещенные символы и т.д.. и вспомогательную шаблонную функцию. S>Функция, как не трудно догадаться, нужна только для того чтобы не нужно было писать тип, которым инстанцируется quoted_t. Если закавычивать надо только строки, то шаблоны и функция она не нужны.
Кавычить нужно только строки, и да, внутренности нужно эскейпить. Ваш подход хорош, но для моего случая имеет пару недостатков:
Слишком общий, такой гибкости мне не надо, т.к. обрабатывать нужно только строки. Ну, это просто исправить.
Всё-таки используется дополнительный класс-обёртка, а его очень хотелось бы избежать. Слишком велик шанс того, что пользователь класса влепит std::string вместо quoted (а то и строковый литерал). Нельзя ли обойтись без дополнительного класса?
Покритикуйте, пожалуйста, упрощённую реализацию. Никаких граблей с неявным преобразованием типов не возникнет?
M>>Проблема №2
S>Тут вопрос — какой уровень автоматизма требуется.
Если быть точным, то хочется так:
std::fstream s("qwe.txt");
s << setindent(2) // Каждая последующая новая строка должна быть отбита 2мя tab-ами.
<< "test" << 123 << std::endl
<< "yet another test" << 567 << std::endl
<< setindent(1) // Каждая последующая новая строка должна быть отбита 1м tab-ом.
<< "test once again" << 890 << std::endl;
и на выходе:
"test" 123
"yet another test" 567
"test once again" 890
M>>Следующее, что подумалось -- boost::iostreams. Ранее с boost-ом сталкиваться не приходилось, и беглый осмотр iostreams не дал результатов. S>Это вообще не про то.
Да вот мне тоже так показалось.
Re[3]: Наследование std::ostream и форматирование вывода
"Madkinder" <8566@users.rsdn.ru> wrote in message news:3342774@news.rsdn.ru...
> > Слишком общий, такой гибкости мне не надо, т.к. обрабатывать нужно только строки. Ну, это просто исправить. > Всё-таки используется дополнительный класс-обёртка, а его очень хотелось бы избежать. Слишком велик шанс того, что пользователь класса влепит std::string вместо quoted (а то и строковый литерал). Нельзя ли обойтись без дополнительного класса? >
Ну тогда только с наследованием, imho. Правда, как мне кажется, проблем при этом не меньше — все равно кто-нибудь может написать
string val = "_value_";
s << "prefix" << val << "suffix";
ожидая получить на выходе "prefix_value_suffix". Ну и до кучи — предположим, как-то удалось решить вашу проблему без наследования и введения лишнего типа, т.е. все строки выводятся во все стримы закавыченными. Вы абсолютно уверены, что у вас в программе не возникнет ситуация, когда в какой-то стрим не понадобится вывести строку без кавычек? Это я к тому, что как-то отделять обычный вывод строк в потоки от "продвинутого" обычно все-таки требуется.
> Покритикуйте, пожалуйста, упрощённую реализацию. Никаких граблей с неявным преобразованием типов не возникнет? >
Наследование от строки — зря, лучше сделать ее членом, копия которая называется s — тоже на мой взгляд лишняя, быстрее будет посимвольно выводить. Больше никаких проблем на первый взгляд не вижу.
> > M>>Проблема №2 > > S>Тут вопрос — какой уровень автоматизма требуется. > Если быть точным, то хочется так: >
> std::fstream s("qwe.txt");
>
> s << setindent(2) // Каждая последующая новая строка должна быть отбита 2мя tab-ами.
> << "test" << 123 << std::endl
> << "yet another test" << 567 << std::endl
> << setindent(1) // Каждая последующая новая строка должна быть отбита 1м tab-ом.
> << "test once again" << 890 << std::endl;
>
> > и на выходе: > >
> "test" 123
> "yet another test" 567
> "test once again" 890
>
Если устроит вместо std::endl писать my::endl или скажем newline, то реализация будет тривиальной. Если все-таки хочется именно std::endl в сочетании именно со стандартными стримами, то лично я ничего посоветовать не могу, поскольку считаю подобные решения хождением по граблям и, соответственно, на практике проделывать такое не пробовал. Хотя через наследование или там фильтры из boost::iostreams наверное получится.
Posted via RSDN NNTP Server 2.1 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[4]: Наследование std::ostream и форматирование вывода
Здравствуйте, Sergey, Вы писали:
S>Ну тогда только с наследованием, imho. Правда, как мне кажется, проблем при этом не меньше — все равно кто-нибудь может написать
string val = "_value_";
s << "prefix" << val << "suffix";
S>Вы абсолютно уверены, что у вас в программе не возникнет ситуация, когда в какой-то стрим не понадобится вывести строку без кавычек?
Это я уже понял ещё при написании прошлого ответа на Ваш пост. Сейчас уже интересна сама возможность реализовать подобное. Даже с наследованием ума не приложу, как можно добиться результата. Ведь наследоваться-то нужно от ostream, а получить функционал в классах stringstream и fstream.
Остановлюсь, пожалуй, на варианте с классом quoted.
S>Наследование от строки — зря, лучше сделать ее членом, копия которая называется s — тоже на мой взгляд лишняя, быстрее будет посимвольно выводить. Больше никаких проблем на первый взгляд не вижу.
Чем чревато наследование от строки? Это запрещено стандартом или где-то могут вылезти другие подводные грабли?
S>Если устроит вместо std::endl писать my::endl или скажем newline, то реализация будет тривиальной.
Возможно я чего-то недопонимаю, ведь my::endl будет добавляться после самой строки, в то время, как табы нужно вставлять до неё. Можно ли пример реализации или более подробное описание подхода?
Спасибо за потраченное время.
Re[5]: Наследование std::ostream и форматирование вывода
"Madkinder" <8566@users.rsdn.ru> wrote in message news:3343032@news.rsdn.ru...
> S>Ну тогда только с наследованием, imho. Правда, как мне кажется, проблем при этом не меньше — все равно кто-нибудь может написать >
> string val = "_value_";
> s << "prefix" << val << "suffix";
>
> S>Вы абсолютно уверены, что у вас в программе не возникнет ситуация, когда в какой-то стрим не понадобится вывести строку без кавычек? > Это я уже понял ещё при написании прошлого ответа на Ваш пост. Сейчас уже интересна сама возможность реализовать подобное. Даже с наследованием ума не приложу, как можно добиться результата. Ведь наследоваться-то нужно от ostream, а получить функционал в классах stringstream и fstream.
Да, это я загнул, наследование тут не при чем, тем более что operator<< для строк вовсе и не член стрима. Однако возможности для хака все равно есть — можно сделать нешаблонную версию под нужные типы — она будет использоваться вместо стандартной шаблонной. Правда, добавлять ее в пространство имен std запрещено, но по рукам-то дать некому...
> S>Наследование от строки — зря, лучше сделать ее членом, копия которая называется s — тоже на мой взгляд лишняя, быстрее будет посимвольно выводить. Больше никаких проблем на первый взгляд не вижу. > Чем чревато наследование от строки? Это запрещено стандартом или где-то могут вылезти другие подводные грабли?
Да ни чем не чревато, чистая вкусовщина (если не рассматривать совсем уж экзотические варианты использования, приводящие к удаления вашей обертки через указатель на строку). Я просто всегда стараюсь использовать максимально слабое кунфу, особенно в библиотечном коде. А раз здесь не требуется, чтобы quoted вел себя как строка, то и не надо этого допускать. А то мало ли как пользователи этот факт используют, потом упаришься совместимость поддерживать.
> S>Если устроит вместо std::endl писать my::endl или скажем newline, то реализация будет тривиальной. > > Возможно я чего-то недопонимаю, ведь my::endl будет добавляться после самой строки, в то время, как табы нужно вставлять до неё. Можно ли пример реализации или более подробное описание подхода?
Ну вообще-то комментарий в setindent гласит — "Каждая последующая новая строка должна быть отбита N tab-ами". Соответственно, достаточно вставлять табы после начала строки в соответствии с текущей установкой setindent. Заводите самопальный фасет
который setindent будет при необходимости добавлять к текущей локали
std::ostream& operator<<(ostream& s, const setindent& v)
{
if (std::has_facet<current_tabbing>(s.getloc()))
std::use_facet<current_tabbing>(s.getloc()).n_ = v.n_;
else
{
current_tabbing *f = new current_tabbing(v.n_);
std::locale l = std::locale(s.getloc(), f);
s.imbue(l);
}
}
и из которого newline будет потом брать n_, чтобы узнать сколько табов выводить.
Хотя тут по-моему вообще имеет смысл уйти от стримов к какой-то вспомогательной обертке над стримами, которая и будет заниматься идентацией и закавычиванием строк.
Posted via RSDN NNTP Server 2.1 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[2]: Наследование std::ostream и форматирование вывода
unspecified behavior или просто бред.
Потому что порядок выполнения автоинкремента, автодекремента и обоих просто подстановок — в данном выражении не определён.
Если компилятор не захочет срезать углы и проинлайнить, то получим отступы 0,0,0,-1 (при передаче ident в operator<< по значению) или 0,0,0,0 (по ссылке).
А если срежет и проинлайнит — получим лотерею.
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Перекуём баги на фичи!
Re[3]: Наследование std::ostream и форматирование вывода
Здравствуйте, Madkinder, Вы писали:
M>Кавычить нужно только строки, и да, внутренности нужно эскейпить. Ваш подход хорош, но для моего случая имеет пару недостатков:
M> M> Слишком общий, такой гибкости мне не надо, т.к. обрабатывать нужно только строки. Ну, это просто исправить. M> Всё-таки используется дополнительный класс-обёртка, а его очень хотелось бы избежать. Слишком велик шанс того, что пользователь класса влепит std::string вместо quoted (а то и строковый литерал). Нельзя ли обойтись без дополнительного класса? M>
Доп.класса-обёртки не хочется, а наследоваться от стандартного — хочется?
Лучше уж пропатчить ostream. (Возможно, унаследоватся от него).
Тогда и строки туда можно будет передавать любые — хоть литералы. И с отступами всё что угодно делать.
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Перекуём баги на фичи!
Re: Наследование std::ostream и форматирование вывода
Здравствуйте, Madkinder, Вы писали:
M>Первое решение, которое пришло в голову -- унаследоваться от std::ostream, добавить методы settablevel(int) и перегрузить ostream& operator<<(ostream&, const & std::string). Но как потом заставить это работать и с fstream и со stringstream, ведь они наследуют ostream, но ни как не мой самопальный класс.
На эту тему посмотри книжку Татьяны Павловской И Юрия Щупака — сборник задач по ОО-программированию на С++ (название не точное), издательство Питер. Там Щупак как раз описывает наследование от стандартного ostream в целях справиться с русскими буквами.
По поводу написания манипулятора — это вполне можно сделать.
В книжке Эккеля есть — см пример сюда:
Написать манипулятор с аргументами несколько сложнее. Для создания манипулятора, совместимого по интерфейсу со стандартными, нужно разобраться в стандартном механизме реализации. Вместо этого можно написать некоторый класс, в котором определить operator<<. Брюс Эккель вслед за Джерри Шварцем, одним из разработчиков объектно-ориентированной библиотеки ввода/вывода, называет такие классы эффекторами. Например, требуется выводить целые значения в двоичном виде. Назовем наш класс binary (листинг 8.13).
Листинг 8.13. «Манипулятор» для двоичного вывода
class binary
{ unsigned long k;
public:
binary( unsigned long k ): k( k ) {}
friend ostream& operator<<( ostream& os, const binary& t );
};
inline ostream& operator<<( ostream& os, const binary& t )
{ const unsigned long MAX = numeric_limits<unsigned long>::max(); // 1unsigned long bit = ~( MAX >> 1 ); // 2while( bit ) { os << ( t.k & bit ? '1' : '0' ); bit >>= 1; }
return os;
}
Такой класс позволяет выводить целые в двоичном виде, например:
short a = 2;
cout << binary( a ) << endl;
Этот класс всегда выводит количество бит, содержащееся в unsigned long, но можно легко преобразовать этот класс в шаблон, который позволит выводить нужное количество бит для беззнаковых целых (листинг 8.14).
Листинг 8.14. Эффектор-шаблон
// опережающие объявленияtemplate <class T> class binary;
template<class T> ostream& operator<<( ostream& os, const binary<T>& t );
// класс-шаблонtemplate <class T>
class binary
{ T k;
public:
binary( T k ): k( k ) {}
friend ostream& operator<< <>( ostream& os, const binary<T>& t );
};
template<class T>
inline
ostream& operator<<( ostream& os, const binary<T>& t )
{ T MAX = numeric_limits<T>::max();
T bit = ~( MAX >> 1 );
while( bit ) { os << ( t.k & bit? '1': '0' ); bit >>= 1; }
return os;
}
Использовать этот класс-шаблон в качестве манипулятора можно так:
short a = -2;
cout << binary<unsigned short>( a ) << endl;
cout << binary<unsigned char>( 128 )<< endl;
На экран выведется
1111111111111110
10000000
Как видим, на экране ровно столько бит, сколько занимают типы unsigned short и unsigned char. Заметим, что использовать в качестве аргумента шаблона знаковый тип нельзя — программа зациклится!
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[3]: Наследование std::ostream и форматирование вывода
" Кодт " <4783@users.rsdn.ru> wrote in message news:3343315@news.rsdn.ru... > Здравствуйте, Sergey, Вы писали: > > S>Тут вопрос — какой уровень автоматизма требуется. Если устраивает что-то вроде > S>
> > unspecified behavior или просто бред. > Потому что порядок выполнения автоинкремента, автодекремента и обоих просто подстановок — в данном выражении не определён. > > Если компилятор не захочет срезать углы и проинлайнить, то получим отступы 0,0,0,-1 (при передаче ident в operator<< по значению) или 0,0,0,0 (по ссылке). > А если срежет и проинлайнит — получим лотерею.
Да ну. Реально ident устроен так (далее он называется indent_t):