Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.97.14.84] |
|
Сообщ.
#1
,
|
|
|
Всем привет!
И так, какой там у нас уже стандарт вышел в С++ ? Что там у нас язык умеет? Например конвертнуть std::string в std::wstring есть возможность сделать это максимально быстро и эффективно? А то я смотрю был функционал с codecvt, но он вроде как deprecated стал(Чем они вообще в своем комитете занимаются?). |
Сообщ.
#2
,
|
|
|
Ему ищут варианты замены. Или делают вид, что ищут. Он deprecated, потому что оказался неудобен, и Комитет не хочет, чтобы много приложений успело его заюзать.
|
Сообщ.
#3
,
|
|
|
Цитата Qraizer @ Ему ищут варианты замены. Или делают вид, что ищут. Он deprecated, потому что оказался неудобен, и Комитет не хочет, чтобы много приложений успело его заюзать. Это пичально А как тогда можно без гемороя вывести в поток std::wostringstream переменную типа std::string ? Добавлено Ладно, нашел прям тут как можно с гемороем делать - Преобразование std::string в std::wstring туда и обратно, как делается? Но все равно это очень пичально я считаю. |
Сообщ.
#4
,
|
|
|
Цитата Wound @ У меня давно уж накиданоА как тогда можно без гемороя вывести в поток std::wostringstream переменную типа std::string ? std::string toNarrow(const std::wstring& str, const std::ctype<wchar_t>& ct = std::use_facet<std::ctype<wchar_t>>(std::locale::classic())); std::wstring toWide (const std::string& str, const std::ctype<wchar_t>& ct = std::use_facet<std::ctype<wchar_t>>(std::locale::classic())); /* -------------------------------------------*/ std::string toNarrow(const std::wstring& str, const std::ctype<wchar_t>& ct) { std::string buf(str.length(), char()); if (!buf.empty()) ct.narrow(str.data(), str.data()+buf.size(), '`', &buf[0]); return std::move(buf); } std::wstring toWide(const std::string& str, const std::ctype<wchar_t>& ct) { std::wstring buf(str.length(), wchar_t()); if (!buf.empty()) ct.widen(str.data(), str.data()+buf.size(), &buf[0]); return std::move(buf); } |
Сообщ.
#5
,
|
|
|
Да я тут просто решил пуститься во все тяжкие, и уже больше пол года на C# опять пишу, ну решил вспомнить былое, да кругозор немного расширить. В итоге от плюсов отвык, а к шарпам привык - там то подобные выкрутасы делаются предельно легко и без напряга. А сейчас нужно было небольшой проект запилить на плюсах, в итоге вот понадобилось в поток выводить сразу и std::string и std::wstring, помню что когда то с codecvt работал и норм было, но так же помню что он deprecated, а из за этого толи предупреждение летело то ли ошибка компиляции уж не помню. Решил поискать, нашел что ниче нема такого. Весьма удивился. Уже вроде какой там? С++24 стандарт пилят? А такой нужной вещи никак не могут ввести в язык.
За этот код тоже спасибо. Завтра прикручу уже. |
Сообщ.
#6
,
|
|
|
В общем, выдалось немного свободного времени, и я порылся в преамбуле вопроса. Могу написать много, но не думаю, что имеет смысл. Так, кратенько.
Во-первых, почему оно вообще появилось. Не думаю, что стоит подробно разъяснять, что потребность в поддержке юникода к C++11 назрела куда сильнее, чем во времена C++98. До 21-го века он фактически ещё только разрабатывался, хотя ревизии выходили регулярно уже с десяток лет, и только где-то с версии 4.0 более-менее стабилизировался. Понятно, что для C++03 поддержка юникода была бы слишком уж большим апгрейдом. Во-вторых. Решили обойтись малой кровью. Не знаю, почему, та и неважно. В целом, std::wstring_convert<>, принимающий фасет перекодирования, вполне нормальное решение, учитывая, что std::codecvt<> уже документировал нужный интерфейс. Было бы неплохо лишь к интерфейсу добавить реализации, что и было сделано в лице std::codecvt_utf8<> (UCS2/4 ⇔ UTF-8), std::codecvt_utf16<> (UCS2/4 ⇔ UTF-16) и std::codecvt_utf8_utf16<> (UTF-8 ⇔ UTF-16). И это даже вполне вменяемо работало и удобно пользовалось. Всего-то запомнить, что .to_bytes() конвертит наш юникод в байты, а .from_bytes() обратно. Была только ИМХО одна проблема, но о ней позже. Но помимо преобразования строк часто также были востребованы ввод/вывод, и Комитет решил поддержать его отдельно, чтоб людям не приходилось конвертить строки в дополнение к вводу/выводу, а чтобы оно само прям во время ввода/вывода. И вот тут и всплыла куча нестыковок. Во-вторых/во-первых. "Внезапно" выяснилось, что Стандарт документирует использование std::codecvt<> только для файловых потоков. То бишь basic_filebuf<> и никак иначе. А так как он прямой потомок std::basic_streambuf<>, который плевать хотел на std::codecvt<>, и это однако строго по Стандарту, то std::codecvt<> выпадают из поля зрения даже наших любимых std::cin, std::cout и иже с ними, не говоря уже о любых производных от std::basic_streambuf<>. Ну, кто пытался "русифицировать консоль" в винде и написать что-то типа codecvt_1251vs866, тот и сам знает. Так что единственно подходящим решением было написать нового потомка от std::basic_streambuf<>, научить его работать с std::codecvt<> и внедрить его между форматирующими std:: (i)(o)stream<> и буферизирующим std::basic_streambuf<>. Так родились std::wbuffer_convert<>. И юзать их предполагалось как-то так: // создаём конвертер из UCS2 в UTF-8 и связываем с его выходом буферизирующий класс консольного вывода std::wbuffer_convert<std::codecvt_utf8<wchar_t>> proxy(std::cout.rdbuf()); // создаём широкий поток вывода и связываем его со входом конвертера std::wostream u8out(&proxy); // вуаля u8out << L"Привет, мир!"; Во-вторых/во-вторых. "Внезапно" выяснилось, что потоки, в отличие от строк, в абсолютно подавляющем большинстве случаев только читаются или только пишутся. Просто потому что чаще всего создаются именно (точнее, производные от них) std::basic_istream<> или std::basic_ostream<>, а вот std::basic_iostream<> огромная редкость. Поэтому, хотя и весьма несложно сконвертить UTF-16 в UTF-8 при <<, то вот сделать наоборот UTF-8 в UTF-16 при том же << уже хренушки, нету такой фичи у этих потоков. Не, ну можно конечно заюзать костыли, но Комитет-то вроде хотел наоборот, избавить народ от костылей. Но это ещё полбеды, вторая полбеда, чья причина описана ниже, приводит к тому, что к выводу std::wbuffer_convert<> можно присобачить только байтовые потоки, т.е. конкретно std::streambuf, а не обобщённый std::basic_streambuf<>. Так что да, с цепочками конвертеров есть нюанс: кроме первого, все остальные должны быть байтовыми конвертерами. Во-вторых/в-третьих. В эпоху С++98 о юникодовых файлах ещё никто не думал. Везде только и делали, что хранили обычные байтовые, при необходимости задействуя многобайтовые кодировки. Собственно, это и нашло отражение в том, что стандартная локаль обязана иметь только std::codecvt<char, char, std::mbstate_t> и std::codecvt<wchar_t, char, std::mbstate_t>, т.е. внешнее представление всегда байтовое. Ненуачё, кому надо, пусть делает std::codecvt<wchar_t, wchar_t, std::mbstate_t> сам, ибо юникод ещё молод и наивен и ничего конкретного потребовать не способен. Так что даже если вы создаёте std::wfilebuf, пусть и опосредовано, посредством std::wofstream, то на выходе в файле всё равно будет последовательность char, так или иначе преобразованных из исходных последовательностей wchar_t, а не эти самые исходные wchar_t, ибо так говорит делать Стандарт. Упс. И ему плевать, был ли над ним некий конвертер, или же ему пришла исходная строка, которую нужно обработать. Вот и получается, что несмотря на то, что если для некоего, скажем, std::ofstream вы создадите конвертер, скажем, из UCS4 в UTF-16 и привяжете его к его std::filebuf, то из-за того, что никто не отменял в локали этого std::ofstream вон тот std::codecvt<wchar_t, char, std::mbstate_t>, и удалить его оттуда вообще невозможно, просто нет такого API, то ваш чудесно подготовленный конвертером вывод в формате UTF-16 не менее чудесно будет убит до char. Хренушки вы получите файл в UTF-16 одним словом без бубнов и танца. Не, ну можно, но за костыли уже говорилось. В-третьих. Короче, <codecvt> был признан неудачным. И даже если ограничиться только std::wstring_convert<>, это будет только половиной решения, т.к. помимо юникода вообще-то существует куда больше региональных кодировок. Десятки. Даже для char ⇔ char их как собак на свалке. То же ANSI⇔OEM, например. А сколько всяких разных латиниц... и их диакретикой, сдвоенными символами итп. Итог. Там готовится что-то сильно масштабнее. Уже далеко не малой кровью. Подозреваю, что хотят в фасетах запилить чуть ли не все кодовые страницы и стандартизировать для этого отдельный интерфейс. Вряд ли мы это увидим даже в C++23. Что касается функционала, объявленного устаревшим, то просто не хотят, чтобы люди на него тратили много внимания в новых проектах. Вряд ли он куда-то денется в будущих релизах Стандарта. Никуда ж не делись std::strstream, например, или там std::plus<>. |
Сообщ.
#7
,
|
|
|
Qraizer, тут еще один момент, который нельзя упускать. Не столько сама сложность работы с перекодировкой, а в какой объем это все выливается. И тут комитет понять можно. Кто пользовался библиотекой ICU - поймет, там одна библиотека весит порядка 25Mb, что почти в 4-ре раза тяжелее STL.
|
Сообщ.
#8
,
|
|
|
Qraizer, жду твоего коммента на свой коммент. Мне это важно
|
Сообщ.
#9
,
|
|
|
Что тут можно откомментить-то. Я пользовался, знаю. Но ведь и задача не настолько глобальная. У ICU жирный API, от STL такого не требуется. + я ещё подозреваю, что никто не будет даже в мыслях на STL перекладывать задачи, которые решаются кучей отдельных и самостоятельных RFC. Вполне достаточно, чтобы, как и в любых других подобных случаях, стандартная библиотека обеспечила лишь кросс-платформенный прокси к API используемой ОС. Ну т.е. если ОС не поддерживает китайские кодовые страницы, ну и ваши аппликухи через STL туда не вхожи, а ежели вдруг надо, просто настройте свою ОСь.
Добавлено Но всё равно объёмы работ над STL весьма немалы. |
Сообщ.
#10
,
|
|
|
Цитата JoeUser @ Но всё равно объёмы работ над STL весьма немалы. Тут да - не вопрос. Но что они с кодировками будут делать, вопрос? Вангую - ничего! Будут или отказываться или динамить. |
Сообщ.
#11
,
|
|
|
Цитата Qraizer @ return std::move(buf); std::move же мешает RVO. Зачем он тут нужен? |
Сообщ.
#12
,
|
|
|
Потому что в данном случае это не обязательное RVO, а необязательное NVRO. Чтобы сделать его обязательным, нужно buf явным образом из lvalue скастовать к rvalue или rvalue ref, что std::move() в общем-то и делает.
В целом не спорю, что это мешает тем компиляторам, которые всё ж делают необязательную оптимизацию. Зато так оптимизация делается, хоть и чуть хуже, но для всех. Хотя вру, в C++17 и такую оптимизацию уже объявили обязательной. Ну что делать, код древний. Нынче std::move() можно смело убрать... наверное. |