Can std::cout work with UTF-8 on Windows?

admin

Administrator
Staff member
I want to make
Code:
std::cout
print an UTF-8 literal. This seems to be an easy task with gcc, but an extremely difficult one with Windows.

The code that I'm trying to get to work is:

Code:
std::cout << "Ελληνικά Русский 你好";

Environment:

<ul>
<li>Windows 10, Visual Studio 2015</li>
<li>Default encoding: 1251</li>
<li>Console encoding: 866</li>
<li>Source encoding: UTF-8 with BOM</li>
</ul>

Requirements:

<ul>
<li>No changes to the line of code itself must be made</li>
<li>Full Unicode range support</li>
<li>Some setup code may be added in the beginning of
Code:
main()
</li>
</ul>

What I've tried:

<ul>
<li>
Code:
#pragma execution_character_set("utf-8")
</li>
<li>
Code:
SetConsoleCP(CP_UTF8); SetConsoleOutputCP(CP_UTF8);
</li>
<li>Set console font to Lucida Console system-wide</li>
<li>
Code:
Use Unicode character set
in project properties</li>
<li>Setup code from <a href="https://alfps.wordpress.com/2011/11/22/unicode-part-1-windows-console-io-approaches/" rel="nofollow noreferrer">this</a> blog</li>
</ul>

Nothing helped, and no StackOverflow answer solved the problem.

<strong>Edit</strong>

To get Unicode <strong>partially</strong> working, do the following:

<ul>
<li>Call
Code:
initStreams()
from the listing below at the start</li>
<li>Turn on
Code:
Use Unicode Character Set
in Project Settings</li>
<li>Add
Code:
/utf-8
option</li>
</ul>

Not working:

<ul>
<li>
Code:
wprintf
</li>
<li>
Code:
cin
/
Code:
wcin
</li>
<li>Chinese characters</li>
</ul>

Code:
initStreams()
implementation:

Code:
#include &lt;cassert&gt;         // assert
#include &lt;codecvt&gt;          // std::codecvt_utf8 (C++11)
#include &lt;stdexcept&gt;        // std::exception
#include &lt;streambuf&gt;        // std::basic_streambuf
#include &lt;iostream&gt;         // std::cout, std::endl
#include &lt;locale&gt;           // std::locale
#include &lt;memory&gt;           // std::unique_ptr (C++11)

#undef  UNICODE
#define UNICODE
#undef  STRICT
#define STRING
#include &lt;windows.h&gt;    // MultiByteToWideChar

class OutputForwarderBuffer : public std::basic_streambuf&lt;char&gt;
{
public:
    using Base = std::basic_streambuf&lt;char&gt;;
    using Traits = Base::traits_type;
    using StreamBuffer = std::basic_streambuf&lt;char&gt;;
    using WideStreamBuffer = std::basic_streambuf&lt;wchar_t&gt;;
    using Base::int_type;
    using Base::char_type;

    OutputForwarderBuffer(
        StreamBuffer&amp; existingBuffer,
        WideStreamBuffer* pWideStreamBuffer
    )
        : Base(existingBuffer)
        , pWideStreamBuffer_(pWideStreamBuffer)
    {
    }

    OutputForwarderBuffer(OutputForwarderBuffer const&amp;) = delete;
    void operator=(OutputForwarderBuffer const&amp;) = delete;

protected:
    std::streamsize xsputn(char const* s, std::streamsize n) override
    {
        if (n == 0) { return 0; }

        int const sourceSize = static_cast&lt;int&gt;(n);
        int const destinationSize = MultiByteToWideChar(CP_UTF8, 0, s, sourceSize, nullptr, 0);
        wideCharBuffer_.resize(static_cast&lt;size_t&gt;(sourceSize));

        int const nWideCharacters = MultiByteToWideChar(CP_UTF8, 0, s, sourceSize, &amp;wideCharBuffer_[0], destinationSize);
        assert(nWideCharacters &gt; 0 &amp;&amp; nWideCharacters == destinationSize);

        return pWideStreamBuffer_-&gt;sputn(&amp;wideCharBuffer_[0], destinationSize);
    }

    int_type overflow(int_type c) override
    {
        bool const cIsEOF = Traits::eq_int_type(c, Traits::eof());
        int_type const failureValue = Traits::eof();
        int_type const successValue = (cIsEOF ? Traits::not_eof(c) : c);

        if (!cIsEOF) {
            char_type const ch = Traits::to_char_type(c);
            std::streamsize const nCharactersWritten = xsputn(&amp;ch, 1);

            return (nCharactersWritten == 1 ? successValue : failureValue);
        }
        return successValue;
    }

private:
    WideStreamBuffer* pWideStreamBuffer_;
    std::wstring wideCharBuffer_;
};

void setUtf8Conversion(std::basic_ios&lt;wchar_t&gt;&amp; stream)
{
    stream.imbue(std::locale(std::locale::empty(), new std::codecvt_utf8_utf16&lt;wchar_t&gt;()));
}

bool isConsole(HANDLE streamHandle)
{
    DWORD consoleMode;
    return !!GetConsoleMode(streamHandle, &amp;consoleMode);
}

bool isConsole(DWORD stdStreamId)
{
    return isConsole(GetStdHandle(stdStreamId));
}

void initStreams()
{
    SetConsoleCP(CP_UTF8);
    SetConsoleOutputCP(CP_UTF8);

    setUtf8Conversion(std::wcout);
    setUtf8Conversion(std::wcerr);
    setUtf8Conversion(std::wclog);

    static OutputForwarderBuffer coutBuffer(*std::cout.rdbuf(), std::wcout.rdbuf());
    static OutputForwarderBuffer cerrBuffer(*std::cerr.rdbuf(), std::wcerr.rdbuf());
    static OutputForwarderBuffer clogBuffer(*std::clog.rdbuf(), std::wclog.rdbuf());

    std::cout.rdbuf(&amp;coutBuffer);
    std::cerr.rdbuf(&amp;cerrBuffer);
    std::clog.rdbuf(&amp;clogBuffer);
}