From fd3c05cd073b11482560d5f0c30bd415ff16ba6f Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 10 Feb 2025 16:00:49 -0800 Subject: [PATCH] Long live qEnvironmentVariableIntegerValue() returning std::optional That way, we won't need to pass a bool pointer to distinguish an environment variable containing zero from one that failed to parse. [ChangeLog][QtCore][QtEnvironment] Added qEnvironmentVariableIntegerValue(), which returns std::optional. Fixes: QTBUG-133654 Change-Id: If0d6c3a6f7a080588fa5fffd87b9365f0f8e1089 Reviewed-by: Edward Welbourne --- .../code/src_corelib_global_qglobal.cpp | 8 +++ src/corelib/global/qtenvironmentvariables.cpp | 67 +++++++++++++++---- src/corelib/global/qtenvironmentvariables.h | 3 + src/gui/kernel/qhighdpiscaling.cpp | 12 +--- .../global/qgetputenv/tst_qgetputenv.cpp | 8 +++ 5 files changed, 74 insertions(+), 24 deletions(-) diff --git a/src/corelib/doc/snippets/code/src_corelib_global_qglobal.cpp b/src/corelib/doc/snippets/code/src_corelib_global_qglobal.cpp index 393bd5778f1..a981163a6e7 100644 --- a/src/corelib/doc/snippets/code/src_corelib_global_qglobal.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_global_qglobal.cpp @@ -653,6 +653,14 @@ bool readConfiguration(const QFile &file) qgetenv(varName).toInt(ok, 0) //! [to-int] +//! [int-value_or] + qEnvironmentVariableIntegerValue(varName).value_or(0) +//! [int-value_or] + +//! [int-eq0] + qEnvironmentVariableIntegerValue(varName) == 0 +//! [int-eq0] + //! [is-null] !qgetenv(varName).isNull() //! [is-null] diff --git a/src/corelib/global/qtenvironmentvariables.cpp b/src/corelib/global/qtenvironmentvariables.cpp index f564a523a57..ae213aa1e39 100644 --- a/src/corelib/global/qtenvironmentvariables.cpp +++ b/src/corelib/global/qtenvironmentvariables.cpp @@ -38,7 +38,7 @@ Q_CONSTINIT static QBasicMutex environmentMutex; On Unix systems, this function is lossless. \sa qputenv(), qEnvironmentVariable(), qEnvironmentVariableIsSet(), - qEnvironmentVariableIsEmpty() + qEnvironmentVariableIsEmpty(), qEnvironmentVariableIntegerValue() */ QByteArray qgetenv(const char *varName) { @@ -104,7 +104,8 @@ QByteArray qgetenv(const char *varName) \note the variable name \a varName must contain only US-ASCII characters. - \sa qputenv(), qgetenv(), qEnvironmentVariableIsSet(), qEnvironmentVariableIsEmpty() + \sa qputenv(), qgetenv(), qEnvironmentVariableIsSet(), qEnvironmentVariableIsEmpty(), + qEnvironmentVariableIntegerValue() */ QString qEnvironmentVariable(const char *varName, const QString &defaultValue) { @@ -192,6 +193,45 @@ bool qEnvironmentVariableIsEmpty(const char *varName) noexcept \sa qgetenv(), qEnvironmentVariable(), qEnvironmentVariableIsSet() */ int qEnvironmentVariableIntValue(const char *varName, bool *ok) noexcept +{ + std::optional value = qEnvironmentVariableIntegerValue(varName); + if (ok) + *ok = bool(value); + return value.value_or(0); +} + +/*! + \relates + \since 6.10 + + Returns the numerical value of the environment variable \a varName. If the + variable is not set or could not be parsed as an integer, it returns + \c{std::nullopt}. + + Similar to + \snippet code/src_corelib_global_qglobal.cpp to-int + except that it's much faster, and can't throw exceptions. + + If a value of zero is semantically the same as an empty or unset variable, + applications can use + \snippet code/src_corelib_global_qglobal.cpp int-value_or + Do note in this case that failures to parse a value will also produce a + zero. + + But if a value of zero can be used to disable some functionality, + applications can compare the returned \c{std::optional} to zero, which will + only be true if the variable was set and contained a number that parsed as + zero, as in: + \snippet code/src_corelib_global_qglobal.cpp int-eq0 + + \note there's a limit on the length of the value, which is sufficient for + all valid values of int, not counting leading zeroes or spaces. Values that + are too long will either be truncated or this function will return + \c{std::nullopt}. + + \sa qgetenv(), qEnvironmentVariable(), qEnvironmentVariableIsSet() +*/ +std::optional qEnvironmentVariableIntegerValue(const char *varName) noexcept { static const int NumBinaryDigitsPerOctalDigit = 3; static const int MaxDigitsForOctalInt = @@ -205,21 +245,19 @@ int qEnvironmentVariableIntValue(const char *varName, bool *ok) noexcept // we provide a buffer that can hold any int value: char buffer[MaxDigitsForOctalInt + 1]; // +1 for the terminating null size_t dummy; - if (getenv_s(&dummy, buffer, sizeof buffer, varName) != 0) { - if (ok) - *ok = false; - return 0; - } + if (getenv_s(&dummy, buffer, sizeof buffer, varName) != 0) + return std::nullopt; size = strlen(buffer); #else const char * const buffer = ::getenv(varName); - if (!buffer || (size = strlen(buffer)) > MaxDigitsForOctalInt) { - if (ok) - *ok = false; - return 0; - } + if (!buffer || (size = strlen(buffer)) > MaxDigitsForOctalInt) + return std::nullopt; #endif - return QByteArrayView(buffer, size).toInt(ok, 0); + bool ok; + int value = QByteArrayView(buffer, size).toInt(&ok, 0); + if (!ok) + return std::nullopt; + return value; } /*! @@ -232,7 +270,8 @@ int qEnvironmentVariableIntValue(const char *varName, bool *ok) noexcept \snippet code/src_corelib_global_qglobal.cpp is-null except that it's potentially much faster, and can't throw exceptions. - \sa qgetenv(), qEnvironmentVariable(), qEnvironmentVariableIsEmpty() + \sa qgetenv(), qEnvironmentVariable(), qEnvironmentVariableIsEmpty(), + qEnvironmentVariableIntegerValue() */ bool qEnvironmentVariableIsSet(const char *varName) noexcept { diff --git a/src/corelib/global/qtenvironmentvariables.h b/src/corelib/global/qtenvironmentvariables.h index e7be1826779..24658c415e5 100644 --- a/src/corelib/global/qtenvironmentvariables.h +++ b/src/corelib/global/qtenvironmentvariables.h @@ -8,6 +8,8 @@ #include #include +#include + #if 0 #pragma qt_class(QtEnvironmentVariables) #pragma qt_sync_stop_processing @@ -32,6 +34,7 @@ Q_CORE_EXPORT bool qunsetenv(const char *varName); Q_CORE_EXPORT bool qEnvironmentVariableIsEmpty(const char *varName) noexcept; Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept; Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept; +Q_CORE_EXPORT std::optional qEnvironmentVariableIntegerValue(const char *varName) noexcept; QT_END_NAMESPACE diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp index 6fead184c2a..9e38353e419 100644 --- a/src/gui/kernel/qhighdpiscaling.cpp +++ b/src/gui/kernel/qhighdpiscaling.cpp @@ -40,14 +40,6 @@ static std::optional qEnvironmentVariableOptionalByteArray(const cha return value.isNull() ? std::nullopt : std::optional(std::move(value)); } -static std::optional qEnvironmentVariableOptionalInt(const char *name) -{ - bool ok = false; - const int value = qEnvironmentVariableIntValue(name, &ok); - auto opt = ok ? std::optional(value) : std::nullopt; - return opt; -} - static std::optional qEnvironmentVariableOptionalReal(const char *name) { const QByteArray val = qgetenv(name); @@ -402,7 +394,7 @@ void QHighDpiScaling::initHighDpiScaling() // Read environment variables static const char* envDebugStr = "environment variable set:"; - std::optional envEnableHighDpiScaling = qEnvironmentVariableOptionalInt(enableHighDpiScalingEnvVar); + std::optional envEnableHighDpiScaling = qEnvironmentVariableIntegerValue(enableHighDpiScalingEnvVar); if (envEnableHighDpiScaling.has_value()) qCDebug(lcHighDpi) << envDebugStr << enableHighDpiScalingEnvVar << envEnableHighDpiScaling.value(); @@ -414,7 +406,7 @@ void QHighDpiScaling::initHighDpiScaling() if (envScreenFactors.has_value()) qCDebug(lcHighDpi) << envDebugStr << screenFactorsEnvVar << envScreenFactors.value(); - std::optional envUsePhysicalDpi = qEnvironmentVariableOptionalInt(usePhysicalDpiEnvVar); + std::optional envUsePhysicalDpi = qEnvironmentVariableIntegerValue(usePhysicalDpiEnvVar); if (envUsePhysicalDpi.has_value()) qCDebug(lcHighDpi) << envDebugStr << usePhysicalDpiEnvVar << envUsePhysicalDpi.value(); diff --git a/tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp b/tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp index 126fdc8ae6e..01be9463813 100644 --- a/tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp +++ b/tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp @@ -28,6 +28,7 @@ void tst_QGetPutEnv::getSetCheck() QVERIFY(!qEnvironmentVariableIsSet(varName)); QVERIFY(qEnvironmentVariableIsEmpty(varName)); + QCOMPARE(qEnvironmentVariableIntegerValue(varName), std::nullopt); ok = true; QCOMPARE(qEnvironmentVariableIntValue(varName), 0); QCOMPARE(qEnvironmentVariableIntValue(varName, &ok), 0); @@ -44,6 +45,7 @@ void tst_QGetPutEnv::getSetCheck() QVERIFY(qEnvironmentVariableIsSet(varName)); QVERIFY(qEnvironmentVariableIsEmpty(varName)); + QCOMPARE(qEnvironmentVariableIntegerValue(varName), std::nullopt); ok = true; QCOMPARE(qEnvironmentVariableIntValue(varName), 0); QCOMPARE(qEnvironmentVariableIntValue(varName, &ok), 0); @@ -68,6 +70,7 @@ void tst_QGetPutEnv::getSetCheck() QVERIFY(qEnvironmentVariableIsSet(varName)); QVERIFY(!qEnvironmentVariableIsEmpty(varName)); + QCOMPARE(qEnvironmentVariableIntegerValue(varName), std::nullopt); ok = true; QCOMPARE(qEnvironmentVariableIntValue(varName), 0); QCOMPARE(qEnvironmentVariableIntValue(varName, &ok), 0); @@ -85,6 +88,7 @@ void tst_QGetPutEnv::getSetCheck() QVERIFY(qunsetenv(varName)); QVERIFY(!qEnvironmentVariableIsSet(varName)); // note: might fail on some systems! QVERIFY(qEnvironmentVariableIsEmpty(varName)); + QCOMPARE(qEnvironmentVariableIntegerValue(varName), std::nullopt); ok = true; QCOMPARE(qEnvironmentVariableIntValue(varName), 0); QCOMPARE(qEnvironmentVariableIntValue(varName, &ok), 0); @@ -210,6 +214,10 @@ void tst_QGetPutEnv::intValue() QCOMPARE(qEnvironmentVariableIntValue(varName), expected); QCOMPARE(qEnvironmentVariableIntValue(varName, &actualOk), expected); QCOMPARE(actualOk, ok); + if (actualOk) + QCOMPARE(qEnvironmentVariableIntegerValue(varName), expected); + else + QCOMPARE(qEnvironmentVariableIntegerValue(varName), std::nullopt); } QTEST_MAIN(tst_QGetPutEnv)