На главную
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
  
> std::string to std::wstring, С++17
    Всем привет!

    И так, какой там у нас уже стандарт вышел в С++ ? Что там у нас язык умеет? Например конвертнуть std::string в std::wstring есть возможность сделать это максимально быстро и эффективно? А то я смотрю был функционал с codecvt, но он вроде как deprecated стал(Чем они вообще в своем комитете занимаются?).
      Ему ищут варианты замены. Или делают вид, что ищут. Он deprecated, потому что оказался неудобен, и Комитет не хочет, чтобы много приложений успело его заюзать.
      Одни с годами умнеют, другие становятся старше.
        Цитата Qraizer @
        Ему ищут варианты замены. Или делают вид, что ищут. Он deprecated, потому что оказался неудобен, и Комитет не хочет, чтобы много приложений успело его заюзать.

        Это пичально :'(
        А как тогда можно без гемороя вывести в поток std::wostringstream переменную типа std::string ?

        Добавлено
        Ладно, нашел прям тут как можно с гемороем делать - Преобразование std::string в std::wstring туда и обратно, как делается?
        Но все равно это очень пичально я считаю.
          Цитата Wound @
          А как тогда можно без гемороя вывести в поток std::wostringstream переменную типа std::string ?
          У меня давно уж накидано
          ExpandedWrap disabled
            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);
            }
          Deprecated там этот codecvt или нет, но оно универсальнее. Но и сложнее, тут с Комитетом не поспоришь. А вообще, пользуй его на здоровье, кто не даёт-то. Окайми в отдельный там namespace к примеру, и как только, так сразу и заменишь на non deprecated.
          Одни с годами умнеют, другие становятся старше.
            Да я тут просто решил пуститься во все тяжкие, и уже больше пол года на C# опять пишу, ну решил вспомнить былое, да кругозор немного расширить. В итоге от плюсов отвык, а к шарпам привык - там то подобные выкрутасы делаются предельно легко и без напряга. А сейчас нужно было небольшой проект запилить на плюсах, в итоге вот понадобилось в поток выводить сразу и std::string и std::wstring, помню что когда то с codecvt работал и норм было, но так же помню что он deprecated, а из за этого толи предупреждение летело то ли ошибка компиляции уж не помню. Решил поискать, нашел что ниче нема такого. Весьма удивился. Уже вроде какой там? С++24 стандарт пилят? А такой нужной вещи никак не могут ввести в язык.
            За этот код тоже спасибо. Завтра прикручу уже.
            Сообщение отредактировано: Wound -
              В общем, выдалось немного свободного времени, и я порылся в преамбуле вопроса. Могу написать много, но не думаю, что имеет смысл. Так, кратенько.
              Во-первых, почему оно вообще появилось. Не думаю, что стоит подробно разъяснять, что потребность в поддержке юникода к 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<>. И юзать их предполагалось как-то так:
              ExpandedWrap disabled
                  // создаём конвертер из UCS2 в UTF-8 и связываем с его выходом буферизирующий класс консольного вывода
                  std::wbuffer_convert<std::codecvt_utf8<wchar_t>> proxy(std::cout.rdbuf());
                  // создаём широкий поток вывода и связываем его со входом конвертера
                  std::wostream u8out(&proxy);                                                
                 
                  // вуаля
                  u8out << L"Привет, мир!";
              Нет никаких дополнительных .to_bytes()/.from_bytes(), ибо и так понятно, что << выводит, а >> вводит, соответственно std::basic_stream<>, ну или как его там, сам будет вызывать нужные std::basic_streambuf<>::overflow()/underflow(). Это также позволяет включать кучу таких конвертеров в цепочку, если вдруг понадобится... с нюансом, правда, но об этом ниже. И это даже тоже прекрасно работало, включая потоки, форматирующие в памяти а-ля std::basic_stringstream<>. Правда, для последних оно как бы без надобности, ибо зачем, если у нас уже есть std::wstring_convert<>.
              Во-вторых/во-вторых. "Внезапно" выяснилось, что потоки, в отличие от строк, в абсолютно подавляющем большинстве случаев только читаются или только пишутся. Просто потому что чаще всего создаются именно (точнее, производные от них) 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<>.
              Сообщение отредактировано: Qraizer -
              Одни с годами умнеют, другие становятся старше.
                Qraizer, тут еще один момент, который нельзя упускать. Не столько сама сложность работы с перекодировкой, а в какой объем это все выливается. И тут комитет понять можно. Кто пользовался библиотекой ICU - поймет, там одна библиотека весит порядка 25Mb, что почти в 4-ре раза тяжелее STL.
                Мои программные ништякиhttps://majestio.info
                  Qraizer, жду твоего коммента на свой коммент. Мне это важно :-?
                  Мои программные ништякиhttps://majestio.info
                    :huh: Что тут можно откомментить-то. Я пользовался, знаю. Но ведь и задача не настолько глобальная. У ICU жирный API, от STL такого не требуется. + я ещё подозреваю, что никто не будет даже в мыслях на STL перекладывать задачи, которые решаются кучей отдельных и самостоятельных RFC. Вполне достаточно, чтобы, как и в любых других подобных случаях, стандартная библиотека обеспечила лишь кросс-платформенный прокси к API используемой ОС. Ну т.е. если ОС не поддерживает китайские кодовые страницы, ну и ваши аппликухи через STL туда не вхожи, а ежели вдруг надо, просто настройте свою ОСь.

                    Добавлено
                    Но всё равно объёмы работ над STL весьма немалы.
                    Сообщение отредактировано: Qraizer -
                    Одни с годами умнеют, другие становятся старше.
                      Цитата JoeUser @
                      Но всё равно объёмы работ над STL весьма немалы.

                      Тут да - не вопрос. Но что они с кодировками будут делать, вопрос? Вангую - ничего! :lol: Будут или отказываться или динамить.
                      Мои программные ништякиhttps://majestio.info
                        Цитата Qraizer @
                        return std::move(buf);

                        std::move же мешает RVO. Зачем он тут нужен?
                        Подпись была включена в связи с окончанием срока наказания
                          Потому что в данном случае это не обязательное RVO, а необязательное NVRO. Чтобы сделать его обязательным, нужно buf явным образом из lvalue скастовать к rvalue или rvalue ref, что std::move() в общем-то и делает.
                          В целом не спорю, что это мешает тем компиляторам, которые всё ж делают необязательную оптимизацию. Зато так оптимизация делается, хоть и чуть хуже, но для всех.

                          Хотя вру, в C++17 и такую оптимизацию уже объявили обязательной. Ну что делать, код древний. Нынче std::move() можно смело убрать... наверное. <_<
                          Сообщение отредактировано: Qraizer -
                          Одни с годами умнеют, другие становятся старше.
                          0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                          0 пользователей:


                          Рейтинг@Mail.ru
                          [ Script Execution time: 0,1316 ]   [ 17 queries used ]   [ Generated: 6.08.20, 09:37 GMT ]