diff --git a/src/corelib/tools/qscopeguard.h b/src/corelib/tools/qscopeguard.h index 40d2747b1d..4d2e715df1 100644 --- a/src/corelib/tools/qscopeguard.h +++ b/src/corelib/tools/qscopeguard.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sérgio Martins +** 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 class QScopeGuard; -template QScopeGuard qScopeGuard(F f); - template 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); }; +#ifdef __cpp_deduction_guides +template QScopeGuard(F(&)()) -> QScopeGuard; +#endif template #if __has_cpp_attribute(nodiscard) Q_REQUIRED_RESULT #endif -QScopeGuard qScopeGuard(F f) +QScopeGuard qScopeGuard(F &&f) { return QScopeGuard(std::move(f)); } +template +#if __has_cpp_attribute(nodiscard) +Q_REQUIRED_RESULT +#endif +QScopeGuard qScopeGuard(const F &f) +{ + return QScopeGuard(f); +} + QT_END_NAMESPACE #endif // QSCOPEGUARD_H diff --git a/src/corelib/tools/qscopeguard.qdoc b/src/corelib/tools/qscopeguard.qdoc index 6b3c942e84..572934d890 100644 --- a/src/corelib/tools/qscopeguard.qdoc +++ b/src/corelib/tools/qscopeguard.qdoc @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sérgio Martins +** 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 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 QScopeGuard::QScopeGuard(F &&f) + \fn template QScopeGuard::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 void QScopeGuard::dismiss() @@ -51,23 +76,17 @@ QT_BEGIN_NAMESPACE of the scope. \ingroup misc - QScopeGuard 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 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 diff --git a/tests/auto/corelib/tools/qscopeguard/qscopeguard.pro b/tests/auto/corelib/tools/qscopeguard/qscopeguard.pro index 070d4b077c..e3645befcf 100644 --- a/tests/auto/corelib/tools/qscopeguard/qscopeguard.pro +++ b/tests/auto/corelib/tools/qscopeguard/qscopeguard.pro @@ -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 diff --git a/tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp b/tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp index 01181ce20e..24c3023183 100644 --- a/tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp +++ b/tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sérgio Martins +** 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 /*! - \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"