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