3
\$\begingroup\$

I was bothered by inability of C++ to mix cout and wcout in the same program - so I've found this question:

Converting between std::wstring and std::string

It didn't help me because it was mostly about MS Windows (I'm on Linux), but thanks to it I've found something similar to the WideCharToMultiByte functions in the C++ standard library.

My goal was just to output wide strings to cout intermittently with regular strings. I've written two variants of conversion function, which take wide string as an argument and return a regular string, containing multi-byte characters, ready to be sent to the cout (provided that the locale is appropriate).

#include <iostream>
#include <string>

using std::cout;
using std::endl;

struct T
{
  std::string a;
  std::wstring b;
};

std::string Convert1(const std::wstring& WS)
// ------ conversion using the wcstombs function 
{
  const unsigned wlen = WS.length();
  char buf[wlen * sizeof(std::wstring::value_type) + 1];
  const ssize_t res = std::wcstombs(buf, WS.c_str(), sizeof(buf));
  return (res >= 0) ? buf : "?";
}

std::string Convert2(const std::wstring& WS)
// ------ conversion using the wcsrtombs function 
{
  mbstate_t st = {};
  const unsigned wlen = WS.length();
  wchar_t wbuf[wlen + 1];
  const size_t copied = WS.copy(wbuf, wlen);
  wbuf[copied] = L'\0';
  const wchar_t* wptr = wbuf;
  char buf[wlen * sizeof(std::wstring::value_type) + 1];
  const ssize_t res = std::wcsrtombs(buf, &wptr, sizeof(buf), &st);
  return (res >= 0) ? buf : "?";
}

int main()
{
  T t = {"Hello", L"Привет"};
  std::setlocale(LC_ALL, "en_US.utf8");
  cout << t.a << endl;
  cout << Convert2(t.b) << endl;
}

It works for me now... Will it work in other setups?

\$\endgroup\$
4
\$\begingroup\$
  • ssize_t is essentially unsigned; that is, the comparison res >= 0 is always true.

    The test should be

    res != static_cast<std::size_t>(-1)
    
  • <cstdlib> and <cwchar> headers are missing

  • You don't need to WS.copy(wbuf, wlen).

    In fact, you don't need wbuf at all:

    const wchar_t * wptr = WS.c_str();
    std::wcsrtombs(buf, &wptr, sizeof(buf), &st);
    

    works just fine.

  • I am not sure that calculating buffer length via sizeof(std::wstring::value_type) is a right way. How can you be sure that multibyte sequence will fit? I'd recommend to obtain required size by passing NULL to wcsrtombs.

PS: И тебе привет

\$\endgroup\$
  • \$\begingroup\$ ssize_t = "signed" size_t \$\endgroup\$ – HEKTO Jun 11 '14 at 20:01
  • \$\begingroup\$ Yes, copy-less variant works! \$\endgroup\$ – HEKTO Jun 11 '14 at 20:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.