LINUX.ORG.RU

Вывод в консоль cout и wcout одновременно

 , ,


1

2

Всем привет.

  1. Я с никогда не понимал фразу: «при первом испоьзовании cout/wcout, всё это хозяйство берёт ориентацию и в дальнейшем нужно не миксовать w… с безпрефиксными вариантами (без w…)». А что это означает на практике? Как происходит эта «ориентация»? Почему миксовать не получается? В общем хочется деталей для понимания.

  2. Первый вопрос вытекает из того, что юзаю ncurses, внутренне она конвертит все входящие символы в wide символы (судя по необходимости устанавливать локаль, когда входящие символы не ASCII). Логично, что и от неё в консоль идут wide символы, и черт его знает что там сориентируется и в какую сторону. А из-за непонимания механизма - всё это какая-то магия.

  3. Ну чисто собственный наивный взгляд - видимо, wcout и cout пишут в один и тот же файл (очевидно да), принимающая сторона хранит символы в wide, и вот ей нужно определиться - конвертить ли входящий поток narrow->wide или нет. Ну если так, то я не могу после ncurses (которая широкая внутренне) печатного цикла использовать cout и мне нужно переоткрывать stdout как пишут в доках. Кто-нибудь не согласен?

Ответ на: удаленный комментарий

Тебя где-то справшивали о том, как с этим жить? Вопрос был об устройстве вывода и причинах по которым файлы приобретают ориентацию. Твою блеяние мимо вообще. Ну и потом - твоя ссылка туфта, ибо http://eel.is/c++draft/iostream.objects.overview#6, а C library говорит:

After a stream is associated with a file, but before any operations are performed on the stream, the stream is without orientation. If a wide-character input or output function is applied to a stream without orientation, the stream becomes wide-oriented. Likewise, if a byte input or output operation is applied to a stream with orientation, the stream becomes byte-oriented. Thereafter, only the fwide() or freopen() functions can alter the orientation of a stream.

Byte input/output functions shall not be applied to a wide-oriented stream and wide-character input/output functions shall not be applied to a byte-oriented stream. 

Слышал, only the fwide() or freopen() functions can alter the orientation of a stream, нет там ничего про способ полоумного анонима с лора.

pavlick ★★ ()

Раз уж тему сделал, смежный вопрос к адекватным участникам: многие клавиши на клаве генерят эскейп последовательности (на уровне io потоков), заметил, что последовательности нестандартизированны вообще никак, нажатие F5 под VT даёт 4 символа, под иксовым ST 5 символов. Как в общих чертах нужно интерпретировать эти последоввательности (видимо терминфо?), может название функции, которой можно последовательность дать. Странно, почему в этой области не появилось никакого стандарта.

pavlick ★★ ()

Токсичная среда. Участников, от которых можно получить ценный совет можно по пальцам пересчитать. С остальными история про бисер. Ок, банюсь.

pavlick ★★ ()

Не уверен, что верно понял вопрос, но выскажу мнение. К сожалению, wcout устарел после появления utf-8. В Linux терминалы либо понимают utf-8 и тогда надо использовать cout и string с utf-8, либо понимают только ASCII.

Если надо, чтобы программа работала ещё и в Windows, то там установленный по умолчанию растровый шрифт не умеет в юникод. Можно хитрить с настройками шрифта, и получить относительно нормальный вывод через wcout, но ввод останется кривой. Для себя я пришёл к выводу, что и в Windows надо использовать string с utf-8, а при вводе-выводе его транслировать в однобайтную текущую кодировку через MultiByteToWideChar, WideCharToMultiByte и GetConsoleCP. Возможно, в десятке что-то улучшилось, но мой подход и там работает.

ncurses используется в линуксовых терминалах, поэтому не ясно для чего там wcout. По идее, там тоже должен быть utf-8, а значит только cout. А локаль устанавливается, чтобы запятую для отделения десятичного знака использовать. Но тут могу ошибаться.

Kogrom ()
Ответ на: комментарий от Kogrom

К сожалению, wcout устарел после появления utf-8.

Он не устарел. Широкие символы - это абстракция над символом, которая может вместить любой символ целиком. Конечно и здесь есть грабли в виде, например, диакритических символов - в итоге на один символ уйдет более одного wchar_t, но всё равно - каждый wchar_t будет представлять полный code point.

В Linux терминалы либо понимают utf-8 и тогда надо использовать cout и string с utf-8, либо понимают только ASCII.

Это не так работает. В том то и дело - мне вообще не надо заморачиваться по поводу кодировки локали. Я просто читаю с stdin, а libc сама конвертируют в эту абстракцию wchar_t, без разницы что там koir-8, utf или windows 1251 ( в программе нужно установить локаль сначала std::locale::global(locale("")) ). Я не говорю, что всё надо делать через wchar_t, как вариант.

ncurses используется в линуксовых терминалах, поэтому не ясно для чего там wcout. По идее, там тоже должен быть utf-8, а значит только cout.

Ну логично исользовать массив с константного размера ячейками, когда мэпишь его на экран, а не массив массивов под переменный utf. Хотя и тут проблемы с теме же диакритическими символами. Вообще юникод пошёл по какому-то странному пути, много лучше было не позволять никаких комбинированнх символов, разное начертание - другая code point. 150К символов в юникоде, utf-8 может закодить грубо 2млрд, неужели нельзя было обойтись без комбинированных символов? А в качестве вишенки всучили ещё precomposed characters для разных вариантов написания одинакового. По-моему, что-то пошло не так.

anonymous ()
Ответ на: комментарий от pavlick

Почему миксовать не получается? В общем хочется деталей для понимания.

Насколько я понимаю, это напрямую связано с наличием внутреннего состояния потоков. Например, если состояние многобайтное, то его нельзя или трудно интерпретировать как юникодное.

Ну если так, то я не могу после ncurses (которая широкая внутренне) печатного цикла использовать cout и мне нужно переоткрывать stdout как пишут в доках. Кто-нибудь не согласен?

ncurses может и не использовать стандартные потоки, либо сделать их копию и использовать её. Стандарт же говорит о потоках, а не файлах, на которые они указывают.

Как в общих чертах нужно интерпретировать эти последоввательности (видимо терминфо?), может название функции, которой можно последовательность дать.

База terminfo сопоставляет последовательности с операциями и наоборот. Есть какие-то стандарты (упоминаются в 1, 2), но вообще всё сложно.

xaizek ★★★★★ ()
Ответ на: комментарий от fsb4000

Мы обсуждаем разновидности костылей. Можно выбрать любые по вкусу. Более здоровым подходом будет использование mintty вместо консоли по умолчанию. Так делает git. Но тут придётся таскать с собой дополнительные 4 мегабайта.

Kogrom ()
Ответ на: комментарий от anonymous

Широкие символы - это абстракция над символом, которая может вместить любой символ целиком.

Я согласен и был бы рад, если бы wchar_t победил. Но победил utf.

Это не так работает.

В теории. А на практике есть терминалы, которые будет выводить непонятно что, если использовать для символа больше одного байта. И не важно, wchar_t или utf-8 у нас будет. Другие будут ожидать, что wchar_t содержит 4 байта, а не 2. Поэтому в Linux остаётся выбирать адекватный терминал и пользоваться uft-8.

По-моему, что-то пошло не так.

Думаю оно так, потому что создатели этой системы сами ей не пользуются. Но нас это устраивает. Иначе бы придумали какую-нибудь РУТФ-8, в которой русские буквы находятся среди первых 128 символов, и терминалы, которые это понимают.

А в качестве вишенки всучили ещё precomposed characters для разных вариантов написания одинакового.

Недавно ради интереса попробовал реализовать 64-битный растровый шрифт. В картинку 8 на 8 влез любой символ с клавиатуры. Размер png на 180 клеток вышел 2,9 килобайт. То есть можно ужать если не до 16 бит, то до 32 точно. Вот вам и кодировка в лоб без всяких комбинаций.

Были ещё такие штуки, как 16-сегментные индикаторы, с помощью которых можно было создать любую латинскую букву и почти все кириллические, кроме Ё, Ц, Щ (их тоже можно, но будет нечитаемо). Правда, это касается только заглавных букв. Но в 32 сегмента можно и маленькие вместить. Возможно, даже несколько бит на цвет останется.

Kogrom ()
Ответ на: комментарий от xaizek

ncurses может и не использовать стандартные потоки, либо сделать их копию и использовать её.

Использует те же, проверял. Копию не делает, но и ориентацию потоку не задает никакую, видимо из-за использования низкоуровневых write() и друзей. В общем-то проще на всякий случай перед каждой инициализацией/деиниц вызывать (на винде работать не будет):

freopen(nullptr, "r", stdin);
freopen(nullptr, "w", stdout);
freopen(nullptr, "w", stderr);

Стандарт же говорит о потоках, а не файлах, на которые они указывают.

И с файлами та же история, у них тоже ориентация, которая задётся при первой записи через высокоуровневый интерфейс, склонен думать, что дело именно в конверсии между широкими символами и локалью силами libc.

anonymous ()

Мне тут ещё вот чего захотелось - сделать какой-то трейс для софтины, но не так, что после кор дампа или в отладчике, а программа чтобы отработала и в процессе скидывала в файл каждый вызов/выход и функции. Погуглил, несколько готовых вариантов не завелись, накостылил даже свой, работает на основе -finstrument-functions, получаю на выходе вроде такого:

begin	initscr
b	newterm
b	new_prescr
end	new_prescr
b	newterm_sp
b	_nc_setupterm
b	_nc_setup_tinfo
b	_nc_read_entry
b	_nc_pathlast
e	_nc_pathlast
b	_nc_first_db
...

меня бы это даже устроил даже не смотря на то, что запускаю под отладчиком и задаю размер таблицы в .dynsym секции руками там же (так не въехал как его узнать иначе). Но есть большой косяк - поделка ище имена символ в таблице экспорта (компилю с -rdynamic), но static функции в данную таблицу не попадают. Может есть готовое решение моей хотелки без данного недостатка?

anonymous ()
Ответ на: комментарий от anonymous

Сделал, как хотел:

G main {
G    initscr {
G       newterm {
G          new_prescr {}
G          newterm_sp {
G             _nc_setupterm {
G                _nc_setup_tinfo {
G                   _nc_read_entry {
G                      _nc_pathlast {}
G                      _nc_first_db {
L                         cache_getenv {
L                            update_getenv {}
                          }
G                         _nc_home_terminfo {}
L                         cache_getenv {
L                            update_getenv {}
                          }
L                         cache_getenv {
L                            update_getenv {}
                          }
...

Всякие статики метятся L, а обычные символы G, ну и формат вывода интуитивно понятный. Вкупе с ltrace можно быстро въехать в логику любой софтины. Надо бы ещё прикрутить awk скрипт для прогона через c++filt для деманглинга плюсовых имён.

PS: если кто подскажет, как можно получть базовый адрес на который был спроецирован ELF экзешника находясь внутри самого экзешника, буду рад, ибо запуск в gdb конечно не сильно в напряг, но когда и без него работает, то лучше. Хотя допускаю, что ничего это не даст т.к. ALSR.

anonymous ()