File indexing completed on 2024-05-05 04:38:47

0001 /*
0002     SPDX-FileCopyrightText: 2017 Christoph Roick <chrisito@gmx.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #ifndef KDEVPLATFORM_SCOPEDDIALOG_H
0008 #define KDEVPLATFORM_SCOPEDDIALOG_H
0009 
0010 #include <QPointer>
0011 
0012 #include <utility>
0013 
0014 namespace KDevelop {
0015 
0016 /// A tag that requests construction of a null ScopedDialog, avoids ambiguity with the variadic template constructor.
0017 struct NullScopedDialog
0018 {
0019     // The explicit default constructor ensures that ambiguous code `ScopedDialog<QDialog> d{{}};` does not compile.
0020     explicit constexpr NullScopedDialog() noexcept = default;
0021 };
0022 
0023 /**
0024  * Wrapper class for QDialogs which should not be instantiated on stack.
0025  *
0026  * Parents of QDialogs may be unintentionally deleted during the execution of the
0027  * dialog and automatically delete their children. When returning to the calling
0028  * function they get intentionally deleted again, which will lead to a crash. This
0029  * can be circumvented by using a QPointer which keeps track of the QDialogs validity.
0030  * See this
0031  * <a href="https://blogs.kde.org/2009/03/26/how-crash-almost-every-qtkde-application-and-how-fix-it-0">blog entry</a>
0032  * for explanation. The ScopedDialog utility allows using the dialog like a
0033  * common pointer.
0034  *
0035  * Instead of
0036  * \code
0037    QFileDialog dlg(this);
0038    if (dlg.exec())
0039        return;
0040   \endcode
0041   simply use
0042  * \code
0043    ScopedDialog<QFileDialog> dlg(this);
0044    if (dlg->exec())
0045        return;
0046   \endcode
0047   without need to manually clean up afterwards.
0048  */
0049 // This class template is final, because inheriting it shouldn't be useful, and
0050 // the forwarding-reference constructor would be invoked in places where slicing
0051 // normally occurs.
0052 template<typename DialogType>
0053 class ScopedDialog final
0054 {
0055     Q_DISABLE_COPY_MOVE(ScopedDialog)
0056 public:
0057     // Explicitly delete unconventional overloads of copy and move constructors
0058     // to prevent a compiler from using the forwarding-reference constructor in
0059     // places where a copy or a move constructor are normally invoked.
0060     ScopedDialog(ScopedDialog&) = delete;
0061     ScopedDialog(const ScopedDialog&&) = delete;
0062 
0063     /// Construct the dialog with any set of allowed arguments
0064     /// for the construction of DialogType
0065     template<typename ... Arguments>
0066     explicit ScopedDialog(Arguments&& ... args)
0067         : ptr{new DialogType(std::forward<Arguments>(args)...)}
0068     {
0069     }
0070     /// Automatically deletes the dialog if it is still present
0071     ~ScopedDialog()
0072     {
0073         delete ptr;
0074     }
0075 
0076     /// Construct a null scoped dialog, i.e. don't create a DialogType but initialize the dialog pointer with nullptr
0077     explicit ScopedDialog(NullScopedDialog)
0078     {
0079     }
0080     /// Destroy the previous dialog if present, then create a dialog with arguments accepted by DialogType()
0081     template<typename... Arguments>
0082     void assign(Arguments&&... args)
0083     {
0084         delete ptr;
0085         ptr = new DialogType(std::forward<Arguments>(args)...);
0086     }
0087 
0088     /// Access the raw pointer to the dialog
0089     DialogType* data() const
0090     {
0091         return ptr;
0092     }
0093     /// Access members of the dialog
0094     DialogType* operator->() const
0095     {
0096         return ptr;
0097     }
0098     /// Access the dialog
0099     DialogType& operator*() const
0100     {
0101         return *ptr;
0102     }
0103     /// Return the corresponding pointer
0104     operator DialogType*() const {
0105         return ptr;
0106     }
0107 
0108 private:
0109     QPointer<DialogType> ptr;
0110 };
0111 
0112 }
0113 
0114 #endif // KDEVPLATFORM_SCOPEDDIALOG_H