QPermission: replace T data<T>() with std::optional<T> value<T>()

As discussed in API review, the default-constructed T() returned from
a mismatched data<T>() call is indistinguishable from a real T with
default state.

To make them distinguishable, return optional<T>. Call the new
function value<T>(), mimicking QVariant::value<T>(), and suggested in
API review, because data() is usually used to return raw pointers, not
values.

Remove the qWarning() on requestedType and actualType mismatch, as the
new function can be used in std::get_if/dynamic_cast-like if-then-else
chains, in which failure is part of the normal operation, and a
warning message misplaced:

  if (auto loc = perm.value<QLocationPermission>())
     ~~~ use *loc ~~~
  else if (auto con = perm.value<QContactsPermission>())
     ~~~ use *con ~~~
  ~~~ etc ~~~

Pick-to: 6.5
Change-Id: I799a58e930307323ebce8f9ac50a42455e9c017f
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Marc Mutz 2023-01-12 11:33:30 +01:00
parent 36619181fb
commit ce104cac50
6 changed files with 42 additions and 36 deletions

View File

@ -211,8 +211,8 @@ Q_LOGGING_CATEGORY(lcPermissions, "qt.permissions", QtWarningMsg);
{
if (permission.status() != Qt::PermissionStatus:Granted)
return;
auto locationPermission = permission.data<QLocationPermission>();
if (locationPermission.accuracy() != QLocationPermission::Precise)
auto locationPermission = permission.value<QLocationPermission>();
if (!locationPermission || locationPermission->accuracy() != QLocationPermission::Precise)
return;
updatePreciseLocation();
}
@ -243,13 +243,12 @@ Q_LOGGING_CATEGORY(lcPermissions, "qt.permissions", QtWarningMsg);
*/
/*!
\fn template <typename T, if_permission<T>> T QPermission::data() const
\fn template <typename T, if_permission<T>> std::optional<T> QPermission::value() const
Returns the \l{typed permission} of type \c T.
Returns the \l{typed permission} of type \c T, or \c{std::nullopt} if this
QPermission object doesn't contain one.
If the type doesn't match the type that was originally used to request the
permission, returns a default-constructed \c T. Use type() for dynamically
choosing which typed permission to request.
Use type() for dynamically choosing which typed permission to request.
This function participates in overload resolution only if \c T is one of
the \l{typed permission} classes:
@ -273,11 +272,8 @@ Q_LOGGING_CATEGORY(lcPermissions, "qt.permissions", QtWarningMsg);
const void *QPermission::data(QMetaType requestedType) const
{
const auto actualType = type();
if (requestedType != actualType) {
qCWarning(lcPermissions, "Cannot convert from %s to %s",
actualType.name(), requestedType.name());
if (requestedType != actualType)
return nullptr;
}
return m_data.data();
}

View File

@ -16,6 +16,8 @@
#include <QtCore/qtypeinfo.h>
#include <QtCore/qmetatype.h>
#include <optional>
#if !defined(Q_QDOC)
QT_REQUIRE_CONFIG(permissions);
#endif
@ -52,12 +54,11 @@ public:
QMetaType type() const { return m_data.metaType(); }
template <typename T, if_permission<T> = true>
T data() const
std::optional<T> value() const
{
if (auto p = data(QMetaType::fromType<T>()))
return *static_cast<const T *>(p);
else
return T{};
return std::nullopt;
}
#ifndef QT_NO_DEBUG_STREAM

View File

@ -53,7 +53,7 @@ static QStringList nativeStringsFromPermission(const QPermission &permission)
{
const auto id = permission.type().id();
if (id == qMetaTypeId<QLocationPermission>()) {
return nativeLocationPermission(permission.data<QLocationPermission>());
return nativeLocationPermission(*permission.value<QLocationPermission>());
} else if (id == qMetaTypeId<QCameraPermission>()) {
return { u"android.permission.CAMERA"_s };
} else if (id == qMetaTypeId<QMicrophonePermission>()) {
@ -63,12 +63,12 @@ static QStringList nativeStringsFromPermission(const QPermission &permission)
return { u"android.permission.BLUETOOTH"_s };
} else if (id == qMetaTypeId<QContactsPermission>()) {
const auto readContactsString = u"android.permission.READ_CONTACTS"_s;
if (!permission.data<QContactsPermission>().isReadWrite())
if (!permission.value<QContactsPermission>()->isReadWrite())
return { readContactsString };
return { readContactsString, u"android.permission.WRITE_CONTACTS"_s };
} else if (id == qMetaTypeId<QCalendarPermission>()) {
const auto readContactsString = u"android.permission.READ_CALENDAR"_s;
if (!permission.data<QCalendarPermission>().isReadWrite())
if (!permission.value<QCalendarPermission>()->isReadWrite())
return { readContactsString };
return { readContactsString, u"android.permission.WRITE_CALENDAR"_s };
}

View File

@ -216,7 +216,7 @@ namespace
Q_ASSERT(!geolocation.isNull());
const auto &permission = geolocationRequestQueue->front().first;
const auto &locationPermission = permission.data<QLocationPermission>();
const auto locationPermission = *permission.value<QLocationPermission>();
const bool highAccuracy = locationPermission.accuracy() == QLocationPermission::Precise;
val options = val::object();

View File

@ -55,7 +55,7 @@ struct PermissionRequest
- (Qt::PermissionStatus)checkPermission:(QPermission)permission
{
const auto locationPermission = permission.data<QLocationPermission>();
const auto locationPermission = *permission.value<QLocationPermission>();
auto status = [self authorizationStatus:locationPermission];
if (status != Qt::PermissionStatus::Granted)
@ -118,7 +118,7 @@ struct PermissionRequest
- (QStringList)usageDescriptionsFor:(QPermission)permission
{
QStringList usageDescriptions = { "NSLocationWhenInUseUsageDescription" };
const auto locationPermission = permission.data<QLocationPermission>();
const auto locationPermission = *permission.value<QLocationPermission>();
if (locationPermission.availability() == QLocationPermission::Always)
usageDescriptions << "NSLocationAlwaysUsageDescription";
return usageDescriptions;
@ -150,7 +150,7 @@ struct PermissionRequest
self.manager.delegate = self;
}
const auto locationPermission = permission.data<QLocationPermission>();
const auto locationPermission = *permission.value<QLocationPermission>();
switch (locationPermission.availability()) {
case QLocationPermission::WhenInUse:
// The documentation specifies that requestWhenInUseAuthorization can

View File

@ -54,11 +54,12 @@ void tst_QPermission::converting_impl() const
QCOMPARE_EQ(p.type(), metaType);
}
// data<>() compiles:
// value<>() compiles:
{
const QPermission p = concrete;
[[maybe_unused]] auto r = p.data<T>();
static_assert(std::is_same_v<decltype(r), T>);
auto v = p.value<T>();
static_assert(std::is_same_v<decltype(v), std::optional<T>>);
QCOMPARE_NE(v, std::nullopt);
}
}
@ -100,35 +101,43 @@ void tst_QPermission::conversionMaintainsState() const
{
p = dummy;
auto r = p.data<DummyPermission>();
auto v = p.value<DummyPermission>();
QCOMPARE_NE(v, std::nullopt);
auto &r = *v;
QCOMPARE_EQ(r.state, dummy.state);
// check mismatched returns default-constructed value:
QCOMPARE_EQ(p.data<QCalendarPermission>().isReadWrite(), cal_default.isReadWrite());
// check mismatched returns nullopt:
QCOMPARE_EQ(p.value<QCalendarPermission>(), std::nullopt);
}
{
p = loc;
auto r = p.data<QLocationPermission>();
auto v = p.value<QLocationPermission>();
QCOMPARE_NE(v, std::nullopt);
auto &r = *v;
QCOMPARE_EQ(r.accuracy(), loc.accuracy());
QCOMPARE_EQ(r.availability(), loc.availability());
// check mismatched returns default-constructed value:
QCOMPARE_EQ(p.data<DummyPermission>().state, dummy_default.state);
// check mismatched returns nullopt:
QCOMPARE_EQ(p.value<DummyPermission>(), std::nullopt);
}
{
p = con;
auto r = p.data<QContactsPermission>();
auto v = p.value<QContactsPermission>();
QCOMPARE_NE(v, std::nullopt);
auto &r = *v;
QCOMPARE_EQ(r.isReadWrite(), con.isReadWrite());
// check mismatched returns default-constructed value:
QCOMPARE_EQ(p.data<QLocationPermission>().accuracy(), loc_default.accuracy());
// check mismatched returns nullopt:
QCOMPARE_EQ(p.value<QLocationPermission>(), std::nullopt);
}
{
p = cal;
auto r = p.data<QCalendarPermission>();
auto v = p.value<QCalendarPermission>();
QCOMPARE_NE(v, std::nullopt);
auto &r = *v;
QCOMPARE_EQ(r.isReadWrite(), cal.isReadWrite());
// check mismatched returns default-constructed value:
QCOMPARE_EQ(p.data<QContactsPermission>().isReadWrite(), con_default.isReadWrite());
// check mismatched returns nullopt:
QCOMPARE_EQ(p.value<QContactsPermission>(), std::nullopt);
}
}