Make Q_ASSERT() usable in constexpr functions

We need Q_ASSERT in (C++11) constexpr functions, and the only way to
inject them in C++11 is to use the comma operator. E.g. in
QLatin1String:

    constexpr QLatin1Char at(int i) const
    { return assert(1 >= 0), assert(i < size()), m_data[i]; }

The main problem with our existing Q_ASSERT is that while it is a
ternary expression in active mode, it was a statement in passive
mode. This is easily fixed by dropping the do-while loop and leaving
just its parenthesized exit condition. Add a cast to void, too,
ensuring that Q_ASSERT has type void in both passive and active modes.

But even in C++14 constexpr functions, which accept several
statements, Q_ASSERT needs to have a path through its conditionals
that is constexpr, but neither qt_assert(_x) nor qt_noop() are
constexpr. Nor can they be in C++11 (no void returns in C++11
constexpr functions). I fixed this by replacing qt_noop() with
static_cast<void>(0). The void cast is required so both 2nd and 3rd
arguments to the ternary are void (mixing void and non-void branches
in the ternary is only allowed if the void leg is a
throw-expression[1]).

As a drive-by, adjust to style guide, remove overparenthesization and
reverse the conditional in the ternary.

Apply it to QLatin1String where we had the problem that constexpr
functions had a narrow constract.

[1] should probably be extended to any [[noreturn]] void function,
e.g. std::terminate().

[ChangeLog][QtCore][QtGlobal] Q_ASSERT() and Q_ASSERT_X() now always
expand to expressions of type void that are usable in constexpr
contexts. This makes them usable in both C++11 and C++14 constexpr
functions.

Change-Id: I09c396bc0034ac344cfaadc6f8cbeb1b7b0cbabc
Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
Marc Mutz 2017-03-14 22:45:05 +01:00
parent a02959bb5b
commit 8ea27bb1c6
2 changed files with 10 additions and 9 deletions

View File

@ -716,9 +716,9 @@ Q_CORE_EXPORT void qt_assert(const char *assertion, const char *file, int line)
#if !defined(Q_ASSERT)
# if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS)
# define Q_ASSERT(cond) do { } while ((false) && (cond))
# define Q_ASSERT(cond) static_cast<void>(false && (cond))
# else
# define Q_ASSERT(cond) ((!(cond)) ? qt_assert(#cond,__FILE__,__LINE__) : qt_noop())
# define Q_ASSERT(cond) ((cond) ? static_cast<void>(0) : qt_assert(#cond, __FILE__, __LINE__))
# endif
#endif
@ -733,9 +733,9 @@ Q_CORE_EXPORT void qt_assert_x(const char *where, const char *what, const char *
#if !defined(Q_ASSERT_X)
# if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS)
# define Q_ASSERT_X(cond, where, what) do { } while ((false) && (cond))
# define Q_ASSERT_X(cond, where, what) static_cast<void>(false && (cond))
# else
# define Q_ASSERT_X(cond, where, what) ((!(cond)) ? qt_assert_x(where, what,__FILE__,__LINE__) : qt_noop())
# define Q_ASSERT_X(cond, where, what) ((cond) ? static_cast<void>(0) : qt_assert_x(where, what, __FILE__, __LINE__))
# endif
#endif

View File

@ -100,7 +100,8 @@ public:
Q_DECL_CONSTEXPR bool isNull() const Q_DECL_NOTHROW { return !data(); }
Q_DECL_CONSTEXPR bool isEmpty() const Q_DECL_NOTHROW { return !size(); }
Q_DECL_CONSTEXPR QLatin1Char at(int i) const { return QLatin1Char(m_data[i]); }
Q_DECL_CONSTEXPR QLatin1Char at(int i) const
{ return Q_ASSERT(i >= 0), Q_ASSERT(i < size()), QLatin1Char(m_data[i]); }
Q_DECL_CONSTEXPR QLatin1Char operator[](int i) const { return at(i); }
using value_type = const char;
@ -125,13 +126,13 @@ public:
const_reverse_iterator crend() const Q_DECL_NOTHROW { return const_reverse_iterator(begin()); }
Q_DECL_CONSTEXPR QLatin1String mid(int pos) const
{ return QLatin1String(m_data + pos, m_size - pos); }
{ return Q_ASSERT(pos >= 0), Q_ASSERT(pos <= size()), QLatin1String(m_data + pos, m_size - pos); }
Q_DECL_CONSTEXPR QLatin1String mid(int pos, int n) const
{ return QLatin1String(m_data + pos, n); }
{ return Q_ASSERT(pos >= 0), Q_ASSERT(n >= 0), Q_ASSERT(pos + n <= size()), QLatin1String(m_data + pos, n); }
Q_DECL_CONSTEXPR QLatin1String left(int n) const
{ return QLatin1String(m_data, n); }
{ return Q_ASSERT(n >= 0), Q_ASSERT(n <= size()), QLatin1String(m_data, n); }
Q_DECL_CONSTEXPR QLatin1String right(int n) const
{ return QLatin1String(m_data + m_size - n, n); }
{ return Q_ASSERT(n >= 0), Q_ASSERT(n <= size()), QLatin1String(m_data + m_size - n, n); }
inline bool operator==(const QString &s) const Q_DECL_NOTHROW;
inline bool operator!=(const QString &s) const Q_DECL_NOTHROW;