Silicomancer Silicomancer - 1 month ago 18
C++ Question

Streaming UTF-8 literals into QTextStream

I am doing the following in a C++ source file that is UTF-8 encoded (Qt 5.7, GCC):

QTextStream textStream(&file);
textStream.setCodec("UTF-8");
textStream << "Copyright ©" << endl;


The resulting file is UTF-8 encoded but the
©
characters is not encoded correctly.

This fails because
operator<<(const char *string)
expects a Latin-1 string literal. Since the source is UTF-8 this doesn't work.

I also know how to work around this:

QTextStream textStream(&file);
textStream.setCodec("UTF-8");
textStream << QString("Copyright ©") << endl;


This works because
QString(const char *str)
expects an UTF-8 string literal.

I need to output a LOT of literal strings this way. I can't wrap all of them into
QString()
.

My questions:

1) Why does
QString
assume UTF-8 but
QTextStream
doesn't? Does this make sense considering Qt sources need to be UTF-8?

2) Is there a way to solve this without hundreds of
QString()
wrappers (or similar)?

Answer

1) It does seem silly, especially if you take this quote from the documentation into consideration:

Internally, QTextStream uses a Unicode based buffer, and QTextCodec is used by QTextStream to automatically support different character sets.

2) You can inherit from QTextStream and provide your own operator<<. I'm not sure whether this is idiomatic Qt, but it works.

struct UnicodedStream : QTextStream
{
    using QTextStream::QTextStream;

    template<typename T>
    UnicodedStream& operator<<(T const& t)
    {
        return static_cast<UnicodedStream&>(static_cast<QTextStream&>(*this) << t);
    }

    UnicodedStream& operator<<(char const* ptr)
    {
        return static_cast<UnicodedStream&>(*this << QString(ptr));
    }
};

And if you don't care about using chained calls like x << "foo" << "bar", you can use a more simplistic version:

struct UnicodedStream : QTextStream
{
    using QTextStream::QTextStream;
    using QTextStream::operator<<;

    QTextStream& operator<<(char const* ptr)
    {
        return *this << QString(ptr);
    }
};