Реально ли сделать такое без залезания в кишки iostreams, как-нибудь в сторонке?
Мне всегда не хватало такого, но как-то не заморачивался. А тут сделал подсистему вывода для своего проекта — попроще в реализации, без развесистой иерархии классов, но с той же концепцией — operator ostream inserter, методы потока и манипуляторы вывода, совместимой с iostreams по всем базовым прикладным вещам: методам потоков, флагам "ios...", манипуляторам и тп — она совместима со стандартной библиотекой. Бонус в том, что не надо много нового изучать, и по большей части можно заглянуть в доку на стандартные потоки и всё там найти.
Так вот, добавил я там немного своих расширений:
манипулятор bin, наравне с hex и dec;
отдельно сделал регистр префикса — манипуляторы uppercasebase, lowercasebase — мне hex'ы всегда нравились в верхнем регистре, а вот префикс (и для bin'ов тоже) — в нижнем — так его хорошо видно, а когда регистр префикса такой же, как у числа, то он сливается с числом и парсить глазами зело труднее;
префикс для двоичных чисел — 0b;
заменил префикс для восьмеричных чисел — с просто нуля на 0o (ноль-О) — чисто для унификации с 0x и 0b;
манипулятор permanent (или как-то так — не помню точно, а исходников сейчас нет под рукой) — насколько я понял из описаний стандартных манипуляторов — они действуют только на один последующий вывод и потом параметры вывода сбрасываются до тех, что установлены для потока. Для потока же постоянные опции устанавливаются при помощи методов потока, через флаги в т.ч. и т.п., что всегда было мне зело неудобно и всегда хотелось устанавливать это манипуляторами. Вот и сделал манипулятор permanent для удобной настройки, он делает все ранее заданные временные опции постоянными. Выглядит так:
cout<<hex<<width(8)<<fill('0')<<permanent;
еще хотелка, думаю, скоро реализую — сделать отдельно binfill, hexfill, octfill, а xfill устанавливает fill char для всех троих, и отдельно decfill — для установки символа заполнения для десятичных чисел — потому как bin/hex обычно хочется дополнять нулями, чтобы понимать, где какой разряд, а в случае с десятичными числами, интересует само число и ведущие нули только мешают (а в случае префиксирования при выводе восьмеричных чисел простым нулем это еще и небольшая мина замедленного действия). Ну и числа вообще, как по мне, должны выравниваться другими символами, нежели строки и прочее нечисловое;
Вообщем, сделал я всё это, и понравилось мне это. И стал я активно пользоваться. Только вот код стал уже непереносим на стандартные потоки. В принципе, у меня для вывода предусмотрен интерфейс типа IWritter, и я сделал биндинги для стандартных потоков, но вот стало интересно, а можно ли стандартные потоки расширить, чтобы реализовать мои хотелки без моих прокладок, а в виде расширений стандартных потоков?
Так-то и то и то вроде как прокладки, но в текущей реализации у меня есть MyOstream и для него биндинги StdOstreamWritter/StdCoutWritter/StdCerrWritter и надо их настраивать перед стартом программы, что неудобно. А хочется, чтобы подключил какой-то хидер — и всё, все фичи присосались автоматом ко всем реализациям std::ostream
M>Реально ли сделать такое без залезания в кишки iostreams, как-нибудь в сторонке?
Ну, посмотри в моей книжке.
В последней главе про ввод-вывод есть параграф "Написание собственных манипуляторов"
Вот отрывок оттуда:
Написать манипулятор с аргументами несколько сложнее. Собственно, для создания манипулятора, совместимого по интерфейсу со стандартными манипуляторами, нужно разобраться в стандартном механизме реализации. Однако вместо этого можно просто написать некоторый класс, для которого, как обычно, определить функцию operator<<. Джерри Шварц назвал такие классы эффекторами (см. также [12]). Например, пусть нам требуется выводить целые значения в двоичном виде. Напишем класс binary (листинг 14.13). Листинг 14.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();
unsigned long bit = ~(MAX >> 1); // старший битwhile(bit) { os << (t.k & bit?'1':'0'); bit >>= 1; }
return os;
}
В функции operator<< самыми важными являются две строки:
const unsigned long MAX = numeric_limits<unsigned long>::max();
unsigned long bit = ~(MAX >> 1); // старший бит
В первой строке в переменную MAX заносится максимально возможное беззнаковое целое число, определенное в стандартном классе числовых пределов numeric_limits.
Как видишь, без залезания внутрь.
Ссыль [12] — это Брюс Эккель. Философия С++, том 2.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[2]: iostreams - вывод чисел в двоичной с/с и прочие расширения
Здравствуйте, LaptevVV, Вы писали:
LVV>Как видишь, без залезания внутрь.
Пример хороший. Но, если честно, то это весьма посредственный манипулятор Я бы его просто назвал классом binary, для которого перегружен оператор вывода в поток. Практически с тем же успехом можно его заменить на функцию с сигнатурой вида std::string binary(unsigned long). Хотя конечно, в приведённом варианте не создаётся временная строка, и это может быть хорошим и желаемым поведением в более сложных случаях.
Но всё же от настоящего манипулятора, наверное, ожидается более богатое поведение — он должен ведь уметь влиять не только на свои параметры, которые ему передали в конструктор, но и на само состояние потока, и на то, как будут форматироваться вывод тех значений, которые выводятся в поток за ним (и вывод которых может производится даже в каком-то другом месте программы, где вообще не известно о существовании этого самого манипулятора. LVV>Ну, посмотри в моей книжке.
Не знаю о какой книжке идёт речь. Но соглашусь, что её стоит посмотреть, если там дальше описывается как с помощью xalloc зарегистрировать новый манипулятор, как через pword правильно сохранять и читать произвольные данные в стандартных потоках, и как через facets применить все эти новые правила к стандартным типам (а не только к новым пользовательским). Жалко, что в приведённом отрывке об этой тёмной стороне потоков ничего нет
Скрытый текст
(впрочем, iostreams прокляты, и лучше с ними вообще не иметь дело, я считаю)
Re[2]: iostreams - вывод чисел в двоичной с/с и прочие расширения
Здравствуйте, LaptevVV, Вы писали:
M>>Реально ли сделать такое без залезания в кишки iostreams, как-нибудь в сторонке? LVV>Ну, посмотри в моей книжке. LVV>В последней главе про ввод-вывод есть параграф "Написание собственных манипуляторов" LVV>Вот отрывок оттуда:
Ну, это ни разу ни манипулятор, а standalone класс для вывода в двоичном виде. Причем его надо явно вызывать. watchmaker уже все написал, добавить нечего. Такие классы я давно писал.
Здравствуйте, Marty, Вы писали:
M>ЗЫ Видимо, мои хотелки нельзя реализовать
В смысле? xalloc + pword и вперёд! Сложно и многословно, да. Но возможно. Вот пример как сделатать собственный манипулятор bin: https://accu.org/index.php/journals/350
Здравствуйте, watchmaker, Вы писали:
M>>ЗЫ Видимо, мои хотелки нельзя реализовать W>В смысле? xalloc + pword и вперёд! Сложно и многословно, да. Но возможно.
Хм. А как с их помощью сделать вывод в двоичном виде для существующих типов?
Здравствуйте, Marty, Вы писали:
M>Здравствуйте! M>Реально ли сделать такое без залезания в кишки iostreams, как-нибудь в сторонке? M>Мне всегда не хватало такого, но как-то не заморачивался.
а в старину проблему вывода в двоичной не знали
test:proc main;
declare
x float(53),
y bit(64) defined(x);
x=12345.6789;
put skip edit('x=',y)(a,b4(16));// вывод в 16-ричном
put skip data(y); // вывод в двоичном
end test;
x=40C81CD6E631F8A0
Y= '0100000011001000000111001101011011100110001100011111100010100000'b