Branch data Line data Source code
1 : : /* 2 : : ** Copyright (C) 2023 Sylvain Fargier 3 : : ** 4 : : ** This software is provided 'as-is', without any express or implied 5 : : ** warranty. In no event will the authors be held liable for any damages 6 : : ** arising from the use of this software. 7 : : ** 8 : : ** Permission is granted to anyone to use this software for any purpose, 9 : : ** including commercial applications, and to alter it and redistribute it 10 : : ** freely, subject to the following restrictions: 11 : : ** 12 : : ** 1. The origin of this software must not be misrepresented; you must not 13 : : ** claim that you wrote the original software. If you use this software 14 : : ** in a product, an acknowledgment in the product documentation would be 15 : : ** appreciated but is not required. 16 : : ** 2. Altered source versions must be plainly marked as such, and must not be 17 : : ** misrepresented as being the original software. 18 : : ** 3. This notice may not be removed or altered from any source distribution. 19 : : ** 20 : : ** Created on: 2023-12-15T11:35:20 21 : : ** Author: Sylvain Fargier <fargier.sylvain@gmail.com> 22 : : */ 23 : : 24 : : #ifndef COW_HPP__ 25 : : #define COW_HPP__ 26 : : 27 : : #include <memory> 28 : : #include <type_traits> 29 : : 30 : : #include <logger/Logger.hpp> 31 : : 32 : : #include "utils.hpp" // convenience, to provide `make_const` 33 : : 34 : : namespace ccut { 35 : : 36 : : /** 37 : : * @brief copy-on-write shared_ptr 38 : : * @details de-referencing the object with non-const operartors will duplicate 39 : : * the object, this object can be used in multi-threaded applications, making 40 : : * the wrapped object thread-safe (with a non-blocking access type). 41 : : * @details object must have a copy-constructor or override `Cow::copy` or 42 : : * `Cow::detach`. 43 : : * @details this object is really close to `std::shared_ptr` but it can't be 44 : : * converted to it (thus the protected inheritance). 45 : : */ 46 : : template<typename T> 47 : : class Cow : protected std::shared_ptr<T> 48 : : { 49 : : public: 50 : : using typename std::shared_ptr<T>::element_type; 51 : : using std::shared_ptr<T>::use_count; 52 : : using std::shared_ptr<T>::operator bool; 53 : : using std::shared_ptr<T>::reset; 54 : : using std::shared_ptr<T>::swap; 55 : : 56 : 23 : Cow() : std::shared_ptr<T>{}, m_isCow{true} {} 57 : 74 : Cow(const Cow &other) : std::shared_ptr<T>{other}, m_isCow{true} {} 58 : 42 : explicit Cow(T *ptr) : std::shared_ptr<T>{ptr}, m_isCow{true} {} 59 : : 60 : : template<typename Deleter> 61 : : Cow(T *ptr, Deleter d) : std::shared_ptr<T>{ptr, d}, m_isCow{true} 62 : : {} 63 : : 64 : : bool operator==(const Cow<T> &other) const { return get() == other.get(); } 65 : 20 : bool operator!=(const Cow<T> &other) const { return get() != other.get(); } 66 : : 67 : : // cppcheck-suppress CastIntegerToAddressAtReturn 68 : 42 : const T *get() const noexcept { return std::shared_ptr<T>::get(); } 69 : : T *get() noexcept 70 : : { 71 : : if (m_isCow) 72 : : detach(); 73 : : // cppcheck-suppress CastIntegerToAddressAtReturn 74 : : return std::shared_ptr<T>::get(); 75 : : } 76 : : 77 : 170 : T *operator->() 78 : : { 79 : 170 : if (m_isCow) 80 : 170 : detach(); 81 : : // cppcheck-suppress CastIntegerToAddressAtReturn 82 : 170 : return std::shared_ptr<T>::operator->(); 83 : : } 84 : : 85 : : // cppcheck-suppress CastIntegerToAddressAtReturn 86 : 443 : const T *operator->() const { return std::shared_ptr<T>::operator->(); } 87 : : 88 : 34 : T &operator*() 89 : : { 90 : 34 : if (m_isCow) 91 : 34 : detach(); 92 : : // cppcheck-suppress returnTempReference 93 : 34 : return std::shared_ptr<T>::operator*(); 94 : : } 95 : : 96 : : // cppcheck-suppress returnTempReference 97 : 35 : const T &operator*() const { return std::shared_ptr<T>::operator*(); } 98 : : 99 : 204 : void detach() 100 : : { 101 : 204 : if (!this->operator bool() || this->use_count() <= 1) 102 : 179 : return; 103 : 25 : this->reset(copy(*std::shared_ptr<T>::get())); 104 : : } 105 : : 106 : : /** 107 : : * @brief Enable/disable the COW feature 108 : : * @details this applies only to this Cow object, it makes the whole chain 109 : : * not thread-safe anymore (use with caution). 110 : : */ 111 : 0 : Cow &setCopyOnWrite(bool enable = true) 112 : : { 113 : 0 : m_isCow = enable; 114 : 0 : return *this; 115 : : } 116 : : 117 : : bool isCopyOnWrite() const { return m_isCow; } 118 : : 119 : : protected: 120 : 24 : static T *copy(const T &t) { return new T(t); } 121 : : 122 : : bool m_isCow; 123 : : }; 124 : : 125 : : template<typename T> 126 : : using cow_ptr = Cow<T>; 127 : : 128 : : /** 129 : : * @brief construct a cow object, similar to `std::make_shared` 130 : : */ 131 : : template<typename T, typename... Args> 132 : 41 : inline cow_ptr<T> make_cow(Args &&...args) 133 : : { 134 : 41 : return cow_ptr<T>(new T(args...)); 135 : : } 136 : : 137 : : template<typename T> 138 : 2 : const logger::Logger &operator<<(const logger::Logger &logger, 139 : : const ccut::cow_ptr<T> &ptr) 140 : : { 141 : 2 : if (logger.isEnabled()) 142 : : { 143 : 2 : if (ptr) 144 : 1 : return logger << *ptr; 145 : : else 146 : 1 : return logger << "nullptr"; 147 : : } 148 : 0 : return logger; 149 : : } 150 : : 151 : : } // namespace ccut 152 : : 153 : : namespace logger { 154 : : 155 : : template<typename T> 156 : : struct exclude_default<ccut::cow_ptr<T>> : std::true_type 157 : : {}; 158 : : 159 : : } // namespace logger 160 : : 161 : : #endif