p-ranav / indicators

Activity Indicators for Modern C++
MIT License
3.03k stars 237 forks source link

Issues with mingw-w64 gcc 10.2.0 on windows 10 #76

Open mcmarius opened 4 years ago

mcmarius commented 4 years ago

Hey, first of all, awesome work! 😃

I ran into some issues when building and running the demo and I would like to share my workarounds.

First error (among many others caused by the missing header)
$ mingw32-make.exe
g++ -pthread -std=c++11 -I../include -o demo demo.cpp
In file included from ../include/indicators/details/stream_helper.hpp:6,
                 from ../include/indicators/progress_bar.hpp:4,
                 from demo.cpp:4:
../include/indicators/termcolor.hpp: In function 'std::ostream& termcolor::red(std::ostream&)':
../include/indicators/termcolor.hpp:287:17: error: 'FOREGROUND_RED' was not declared in this scope
  287 |                 FOREGROUND_RED
      |                 ^~~~~~~~~~~~~~
../include/indicators/termcolor.hpp:287:17: note: the macro 'FOREGROUND_RED' had not yet been defined
In file included from c:\winlibs-x86_64-posix-seh-gcc-10.2.0-llvm-11.0.0-mingw-w64-8.0.0-r3\mingw64\x86_64-w64-mingw32\include\windows.h:74,
                 from ../include/indicators/terminal_size.hpp:7,
                 from ../include/indicators/progress_bar.hpp:12,
                 from demo.cpp:4:
c:\winlibs-x86_64-posix-seh-gcc-10.2.0-llvm-11.0.0-mingw-w64-8.0.0-r3\mingw64\x86_64-w64-mingw32\include\wincon.h:121: note: it was later defined here
  121 | #define FOREGROUND_RED 0x4

In indicators/termcolor.hpp, some Windows-specific headers are not included. I changed lines 36-44 (moved an endif) to this:

#elif defined(TERMCOLOR_OS_WINDOWS)
#if defined(_MSC_VER)
#if !defined(NOMINMAX)
#define NOMINMAX
#endif
#endif
#   include <io.h>
#   include <windows.h>
#endif

The second issue encountered was the from_bytes conversion in function std::wstring utf8_decode(const std::string &str) from indicators/display_width.hpp. I replaced it with the function from this answer. Though I'm not sure if this second workaround is portable.

Second issue appears when using an emoji as lead in the nested progress bar example
$ ./demo.exe
[■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ ] Buying more snacks
Reading package list... Done
[==================================================] Done
{ 97% } ⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠸⠧ Restoring system state
{ ERROR } 98% Failed to restore system
[■-------------------------------------------------] Reverting system restore
⡀ 101% Checking credentials
✔ Authenticated!
  Compiling mission
 - ✔ Searching for the Moon
 - ✔ Contacting Kerbal headquarters
 - ✔ Designing spaceship
 - ✔ Launching rocket
 - 🌎terminate called after throwing an instance of 'std::range_error'
  what():  wstring_convert::from_bytes
Also posting it here if the stackoverflow answer gets deleted
// credits: https://stackoverflow.com/a/7154226
static inline std::wstring utf8_decode(const std::string& utf8)
{
    std::vector unicode;
    size_t i = 0;
    while (i < utf8.size())
    {
        unsigned long uni;
        size_t todo;
        bool error = false;
        unsigned char ch = utf8[i++];
        if (ch <= 0x7F)
        {
            uni = ch;
            todo = 0;
        }
        else if (ch <= 0xBF)
        {
            throw std::logic_error("not a UTF-8 string");
        }
        else if (ch <= 0xDF)
        {
            uni = ch&0x1F;
            todo = 1;
        }
        else if (ch <= 0xEF)
        {
            uni = ch&0x0F;
            todo = 2;
        }
        else if (ch <= 0xF7)
        {
            uni = ch&0x07;
            todo = 3;
        }
        else
        {
            throw std::logic_error("not a UTF-8 string");
        }
        for (size_t j = 0; j < todo; ++j)
        {
            if (i == utf8.size())
                throw std::logic_error("not a UTF-8 string");
            unsigned char ch = utf8[i++];
            if (ch < 0x80 || ch > 0xBF)
                throw std::logic_error("not a UTF-8 string");
            uni <<= 6;
            uni += ch & 0x3F;
        }
        if (uni >= 0xD800 && uni <= 0xDFFF)
            throw std::logic_error("not a UTF-8 string");
        if (uni > 0x10FFFF)
            throw std::logic_error("not a UTF-8 string");
        unicode.push_back(uni);
    }
    std::wstring utf16;
    for (size_t i = 0; i < unicode.size(); ++i)
    {
        unsigned long uni = unicode[i];
        if (uni <= 0xFFFF)
        {
            utf16 += (wchar_t)uni;
        }
        else
        {
            uni -= 0x10000;
            utf16 += (wchar_t)((uni >> 10) + 0xD800);
            utf16 += (wchar_t)((uni & 0x3FF) + 0xDC00);
        }
    }
    return utf16;
}

An #include <vector> would also be necessary for the above workaround.

My current environment is the following:

p-ranav commented 3 years ago

@mcmarius

The second issue encountered was the from_bytes conversion in function std::wstring utf8_decode(const std::string &str) from indicators/display_width.hpp

Can you describe the issue that was encountered?

mcmarius commented 3 years ago

It seems that std::wstring_convert::from_bytes does not like 4-byte characters on Windows with gcc and throws an exception:

terminate called after throwing an instance of 'std::range_error'
  what():  wstring_convert::from_bytes

It is called in the return statement of utf8_decode in indicators/display_width.hpp:285. It works fine on WSL now, but still throws an exception when compiling the demo with gcc MinGW. It works with 3-byte emojis such as ⌚️. Replacing utf8_decode with the function in the linked SO answer seems to solve the issue.

With Terminus it works as expected, but there is another issue with 3-byte emojis using windows terminal which reproduces both on powershell profile and on wsl. This might be the fault of Windows Terminal though: image

All emojis up to U+329x seem to exibit this behaviour (extra newlines), while emojis from U+1F00x seem to break on Windows without the mentioned fix (I picked some emojis randomly from this wikipedia article).