简体   繁体   中英

Convert a QString to a const char*

For the purpose of creating a custom exception class, I need to convert a QString to a const char* . Here's the main code of the aforementioned class:

// File "Exception.cpp"

const auto MESSAGE_PREFIX = QStringLiteral("Exception ");

Exception::Exception(const char* file, int line, const QString& cause)
    : m_Message{MESSAGE_PREFIX + file + ':' + QString::number(line) + ": " + cause}
{
}

const char* Exception::what() const noexcept
{
    // QString -> const char*
    const auto ba = m_Message.toLocal8Bit();
    return ba.constData();
}

So, the conversion happens in the overriden method Exception::what and the returned C-string indicates, among others, the file which throws the exception.

In addition, I've defined a macro THROW_EXCEPTION_IF() which helps throwing exception:

// File "exception_macros.h"

#include "Exception.h"

#ifdef Q_OS_WINDOWS
#define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
#else
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#endif

#define THROW_EXCEPTION_IF(condition, cause)
    if (condition) {
        auto str = QString{};
        QTextStream stream{&str};
        stream << cause;
        throw Exception{__FILENAME__, __LINE__, str};
    }

Finally, I test the above macro with the following code:

void TestException::testMacro()
{
    try {
        THROW_EXCEPTION_IF(true, "Test")
        QVERIFY(false);
   }
   catch (const std::exception& e) {
       QVERIFY(true);
       QCOMPARE(QString{e.what()}, QStringLiteral("Exception TestException.cpp:36: Test"));
   }
}

And here's the problem: when I run this test on Linux (Qt 5.7.1, GCC 6.3), it fails with this message:

FAIL!: TestException::testMacro() Compared values are not the same
Actual (QString{e.what()}): "Exception TestExn\\T\e\s\t\E\x\n\\\" Expected (QStringLiteral("Exception TestException.cpp:36: Test")): "Exception TestException.cpp:36: Test"

And it's the same problem on Windows (Qt 5.15) with MSVC 2019 but it works with MinGW 8.1 . Moreover, on Linux, when I replace m_Message.toLocal8bit() with m_Message.toLatin1() , the test passed successfully. I think there's a problem with Unicode characters but I don't understand where's the problem in my code. Thank yout very much for your help.

Your problem is that you are returning an invalid pointer:

const auto ba = m_Message.toLocal8Bit(); // here you create a bytearray
return ba.constData();//here you take the pointer to the data of the bytearray
                      //then you destroy the array, hence the pointer.
                      //then you return an invalid pointer

You have different behaviors in the different platforms because it is not warrantied when the pointer will not be longer available. It depends on the platform, the compiler, the compilation flags, etc.

Why it doesn't work

I agree with Pablo Yaggi that you are trying to access destroyed data, see QByteArray::constData :

The [ const char * ] pointer remains valid as long as the byte array isn't reallocated or destroyed.

Solution

You should store the QByteArray (returned by toLocal8Bit ) in your exception class (instead of the QString ). Note that you may want to return the string UTF-8 encoded (see toUtf8 ) to support all (special) characters. The behaviour of toLocal8Bit and toLatin1 are undefined if unsupported characters are encountered:

If this string contains any characters that cannot be encoded in the locale, the returned byte array is undefined. Those characters may be suppressed or replaced by another. [source]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM