File indexing completed on 2024-05-12 15:57:03

0001 /*
0002  *  SPDX-FileCopyrightText: 2018 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #ifndef KISLAZYSTORAGE_H
0008 #define KISLAZYSTORAGE_H
0009 
0010 #include <functional>
0011 #include <memory>
0012 #include <atomic>
0013 
0014 #include <QMutex>
0015 #include <QMutexLocker>
0016 #include "KisMpl.h"
0017 
0018 /**
0019  * KisLazyStorage is a special class implementing some kind
0020  * of lazy evaluation. It mimics the behaviorof a normal
0021  * pointer type, but creates `T` only on the first trt to
0022  * dereference this pointer.
0023  */
0024 template <typename T, typename... Args>
0025 class KisLazyStorage
0026 {
0027 public:
0028     struct init_value_tag { explicit init_value_tag() = default; };
0029 
0030     /**
0031      * Create a storage with a deferred creation of the object
0032      * `T`. The arguments \p args are passed to the constructor
0033      * of `T` on the first dereference operation on the storage.
0034      */
0035     explicit KisLazyStorage(Args... args)
0036         : m_constructionArgs(std::forward<Args>(args)...),
0037           m_data(0)
0038     {
0039     }
0040 
0041     /**
0042      * Create a storage and initialize it with \p value
0043      * right away without any deferring. Please take it into
0044      * account that the storage will still store a default-
0045      * initialized values of types `Args...` in an internal
0046      * tuple. If you want to avoid this default-construction,
0047      * then just wrap the arguments into std::optional.
0048      */
0049     explicit KisLazyStorage(init_value_tag, T &&value)
0050         : m_data(new T(std::forward<T>(value)))
0051     {
0052     }
0053 
0054     KisLazyStorage(const KisLazyStorage &rgh) = delete;
0055     KisLazyStorage(KisLazyStorage &&rgh) = delete;
0056 
0057 
0058     ~KisLazyStorage() {
0059         delete m_data.load();
0060     }
0061 
0062     T* operator->() {
0063         return getPointer();
0064     }
0065 
0066     T& operator*() {
0067         return *getPointer();
0068     }
0069 
0070 private:
0071     T* getPointer() {
0072         if(!m_data) {
0073             QMutexLocker l(&m_mutex);
0074             if(!m_data) {
0075                 m_data = kismpl::apply_r<T*>(&constructObject, m_constructionArgs);
0076             }
0077         }
0078         return m_data;
0079     }
0080 
0081     static inline T* constructObject(Args... args) {
0082         return new T(std::forward<Args>(args)...);
0083     }
0084 
0085 private:
0086     std::tuple<Args...> m_constructionArgs;
0087     std::atomic<T*> m_data;
0088     QMutex m_mutex;
0089 };
0090 
0091 #endif // KISLAZYSTORAGE_H