File indexing completed on 2024-05-19 04:25:09

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 #include <mutex>
0014 
0015 #include "KisMpl.h"
0016 
0017 /**
0018  * KisLazyStorage is a special class implementing some kind
0019  * of lazy evaluation. It mimics the behavior of a normal
0020  * pointer type, but creates `T` only on the first trt to
0021  * dereference this pointer.
0022  */
0023 template <typename T, typename... Args>
0024 class KisLazyStorage
0025 {
0026 public:
0027     struct init_value_tag { explicit init_value_tag() = default; };
0028 
0029     /**
0030      * Create a storage with a deferred creation of the object
0031      * `T`. The arguments \p args are passed to the constructor
0032      * of `T` on the first dereference operation on the storage.
0033      */
0034     explicit KisLazyStorage(Args... args)
0035         : m_constructionArgs(std::forward<Args>(args)...),
0036           m_data(0)
0037     {
0038     }
0039 
0040     /**
0041      * Create a storage and initialize it with \p value
0042      * right away without any deferring. Please take it into
0043      * account that the storage will still store a default-
0044      * initialized values of types `Args...` in an internal
0045      * tuple. If you want to avoid this default-construction,
0046      * then just wrap the arguments into std::optional.
0047      */
0048     explicit KisLazyStorage(init_value_tag, T &&value)
0049         : m_data(new T(std::forward<T>(value)))
0050     {
0051     }
0052 
0053     KisLazyStorage(const KisLazyStorage &rhs) = delete;
0054     KisLazyStorage& operator=(const KisLazyStorage &rhs) = delete;
0055 
0056     KisLazyStorage(KisLazyStorage &&rhs) {
0057         *this = std::move(rhs);
0058     }
0059 
0060     KisLazyStorage& operator=(KisLazyStorage &&rhs) {
0061         std::scoped_lock lock(m_mutex, rhs.m_mutex);
0062 
0063         m_constructionArgs = std::move(rhs.m_constructionArgs);
0064         delete m_data.load();
0065         m_data.store(rhs.m_data.load());
0066         rhs.m_data.store(0);
0067 
0068         return *this;
0069     }
0070 
0071     ~KisLazyStorage() {
0072         delete m_data.load();
0073     }
0074 
0075     T* operator->() {
0076         return getPointer();
0077     }
0078 
0079     T& operator*() {
0080         return *getPointer();
0081     }
0082 
0083 private:
0084     T* getPointer() {
0085         if(!m_data) {
0086             std::unique_lock l(m_mutex);
0087             if(!m_data) {
0088                 m_data = std::apply(&constructObject, m_constructionArgs);
0089             }
0090         }
0091         return m_data;
0092     }
0093 
0094     static inline T* constructObject(Args... args) {
0095         return new T(std::forward<Args>(args)...);
0096     }
0097 
0098 private:
0099     std::tuple<Args...> m_constructionArgs;
0100     std::atomic<T*> m_data;
0101     std::mutex m_mutex;
0102 };
0103 
0104 #endif // KISLAZYSTORAGE_H