QScopeGuard: Make constructor public

With Class Template Argument Deduction users might want to use the constructor
itself instead of a separate helper function. In both cases it's possible to let
the compiler deduce the template arguments.

Try to make the usefulness of the helper function in the absence of CTAD still
clear in the documentation.

Change-Id: I9b07983c1fb276a6dd9e7ed4c3e606764e9b68ca
Reviewed-by: Ville Voutilainen <ville.voutilainen@qt.io>
This commit is contained in:
Kari Oikarinen 2019-11-11 13:35:53 +02:00
parent 601ce9e08a
commit 4f077b7e5f
4 changed files with 155 additions and 29 deletions

View File

@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sérgio Martins <sergio.martins@kdab.com>
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@ -45,10 +46,6 @@
QT_BEGIN_NAMESPACE
template <typename F> class QScopeGuard;
template <typename F> QScopeGuard<F> qScopeGuard(F f);
template <typename F>
class
#if __has_cpp_attribute(nodiscard)
@ -59,13 +56,23 @@ Q_REQUIRED_RESULT
QScopeGuard
{
public:
explicit QScopeGuard(F &&f) noexcept
: m_func(std::move(f))
{
}
explicit QScopeGuard(const F &f) noexcept
: m_func(f)
{
}
QScopeGuard(QScopeGuard &&other) noexcept
: m_func(std::move(other.m_func))
, m_invoke(qExchange(other.m_invoke, false))
{
}
~QScopeGuard()
~QScopeGuard() noexcept
{
if (m_invoke)
m_func();
@ -77,28 +84,34 @@ public:
}
private:
explicit QScopeGuard(F &&f) noexcept
: m_func(std::move(f))
{
}
Q_DISABLE_COPY(QScopeGuard)
F m_func;
bool m_invoke = true;
friend QScopeGuard qScopeGuard<F>(F);
};
#ifdef __cpp_deduction_guides
template <typename F> QScopeGuard(F(&)()) -> QScopeGuard<F(*)()>;
#endif
template <typename F>
#if __has_cpp_attribute(nodiscard)
Q_REQUIRED_RESULT
#endif
QScopeGuard<F> qScopeGuard(F f)
QScopeGuard<F> qScopeGuard(F &&f)
{
return QScopeGuard<F>(std::move(f));
}
template <typename F>
#if __has_cpp_attribute(nodiscard)
Q_REQUIRED_RESULT
#endif
QScopeGuard<F> qScopeGuard(const F &f)
{
return QScopeGuard<F>(f);
}
QT_END_NAMESPACE
#endif // QSCOPEGUARD_H

View File

@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sérgio Martins <sergio.martins@kdab.com>
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@ -35,6 +36,30 @@ QT_BEGIN_NAMESPACE
\inmodule QtCore
\brief Provides a scope guard for calling a function at the end of
a scope.
QScopeGuard<F> is a class of which the sole purpose is to run the function
\a f in its destructor. This is useful for guaranteeing
your cleanup code is executed, whether the function is exited normally,
exited early by a return statement, or exited by an exception.
\note Exceptions are not supported. The callable shouldn't throw when
executed, copied or moved.
\sa QScopedValueRollback
*/
/*!
\fn template <typename F> QScopeGuard<F>::QScopeGuard(F &&f)
\fn template <typename F> QScopeGuard<F>::QScopeGuard(const F &f)
Create a scope guard that will execute \a f at the end of the scope.
If \e F is a lambda, its type cannot be written. In that case you need to
either rely on class template argument deduction (C++17 feature) and leave
the template parameter out completely or use the helper function
qScopeGuard() instead of this constructor.
\since 5.15
*/
/*! \fn template <typename F> void QScopeGuard<F>::dismiss()
@ -51,23 +76,17 @@ QT_BEGIN_NAMESPACE
of the scope.
\ingroup misc
QScopeGuard<F> is a class of which the sole purpose is to run the function
\a f in its destructor. This is useful for guaranteeing
your cleanup code is executed, whether the function is exited normally,
exited early by a return statement, or exited by an exception.
Create a scope guard that will execute \a f at the end of the scope.
If \e F is a lambda then you cannot instantiate the template directly,
therefore the qScopeGuard() helper is provided and QScopeGuard<F> is made a
private implementation detail.
This helper function is provided so that you can easily construct a
QScopeGuard without having to name the template parameter for the type of
the callable. If \e F is a lambda then you cannot write its type and relying
on this helper or class template argument deduction is necessary.
Example usage is as follows:
\snippet code/src_corelib_tools_qscopeguard.cpp 0
\note Exceptions are not supported. The callable shouldn't throw when
executed, copied or moved.
\sa QScopedValueRollback
*/
QT_END_NAMESPACE

View File

@ -2,3 +2,6 @@ CONFIG += testcase
TARGET = tst_qscopeguard
QT = core testlib
SOURCES = tst_qscopeguard.cpp
# Force C++17 if available
contains(QT_CONFIG, c++1z): CONFIG += c++1z

View File

@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sérgio Martins <sergio.martins@kdab.com>
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
@ -30,24 +31,114 @@
#include <QtCore/QScopeGuard>
/*!
\class tst_QScopedGuard
\class tst_QScopeGuard
\internal
\since 5.11
\brief Tests class QScopedCleanup and function qScopeGuard
\brief Tests class QScopeGuard and function qScopeGuard
*/
class tst_QScopedGuard : public QObject
class tst_QScopeGuard : public QObject
{
Q_OBJECT
private Q_SLOTS:
void construction();
void constructionFromLvalue();
void constructionFromRvalue();
void leavingScope();
void exceptions();
};
void func()
{
}
int intFunc()
{
return 0;
}
Q_REQUIRED_RESULT int noDiscardFunc()
{
return 0;
}
struct Callable
{
Callable() { }
Callable(const Callable &other)
{
Q_UNUSED(other);
++copied;
}
Callable(Callable &&other)
{
Q_UNUSED(other);
++moved;
}
void operator()() { }
static int copied;
static int moved;
static void resetCounts()
{
copied = 0;
moved = 0;
}
};
int Callable::copied = 0;
int Callable::moved = 0;
static int s_globalState = 0;
void tst_QScopedGuard::leavingScope()
void tst_QScopeGuard::construction()
{
#ifdef __cpp_deduction_guides
QScopeGuard fromLambda([] { });
QScopeGuard fromFunction(func);
QScopeGuard fromFunctionPointer(&func);
QScopeGuard fromNonVoidFunction(intFunc);
QScopeGuard fromNoDiscardFunction(noDiscardFunc);
QScopeGuard fromStdFunction{std::function(func)};
std::function stdFunction(func);
QScopeGuard fromNamedStdFunction(stdFunction);
#else
QSKIP("This test requires C++17 Class Template Argument Deduction support enabled in the compiler.");
#endif
}
void tst_QScopeGuard::constructionFromLvalue()
{
#ifdef __cpp_deduction_guides
Callable::resetCounts();
Callable callable;
{
QScopeGuard guard(callable);
}
QCOMPARE(Callable::copied, 1);
QCOMPARE(Callable::moved, 0);
#else
QSKIP("This test requires C++17 Class Template Argument Deduction support enabled in the compiler.");
#endif
}
void tst_QScopeGuard::constructionFromRvalue()
{
#ifdef __cpp_deduction_guides
Callable::resetCounts();
{
Callable callable;
QScopeGuard guard(std::move(callable));
}
QCOMPARE(Callable::copied, 0);
QCOMPARE(Callable::moved, 1);
#else
QSKIP("This test requires C++17 Class Template Argument Deduction support enabled in the compiler.");
#endif
}
void tst_QScopeGuard::leavingScope()
{
auto cleanup = qScopeGuard([] { s_globalState++; QCOMPARE(s_globalState, 3); });
QCOMPARE(s_globalState, 0);
@ -61,7 +152,7 @@ void tst_QScopedGuard::leavingScope()
s_globalState++;
}
void tst_QScopedGuard::exceptions()
void tst_QScopeGuard::exceptions()
{
s_globalState = 0;
bool caught = false;
@ -81,5 +172,5 @@ void tst_QScopedGuard::exceptions()
}
QTEST_MAIN(tst_QScopedGuard)
QTEST_MAIN(tst_QScopeGuard)
#include "tst_qscopeguard.moc"