|
11 | 11 | #include "Framework/Variant.h" |
12 | 12 | #include "Framework/VariantPropertyTreeHelpers.h" |
13 | 13 | #include "Framework/VariantJSONHelpers.h" |
| 14 | +#include <cstdlib> |
| 15 | +#include <cstring> |
14 | 16 | #include <iostream> |
| 17 | +#include <memory> |
15 | 18 | #include <sstream> |
16 | 19 |
|
17 | 20 | namespace o2::framework |
@@ -84,145 +87,166 @@ std::string Variant::asString() const |
84 | 87 | return ss.str(); |
85 | 88 | } |
86 | 89 |
|
87 | | -Variant::Variant(const Variant& other) : mType(other.mType) |
| 90 | +namespace |
88 | 91 | { |
89 | | - // In case this is an array we need to duplicate it to avoid |
90 | | - // double deletion. |
91 | | - switch (mType) { |
92 | | - case VariantType::String: |
93 | | - mSize = other.mSize; |
94 | | - variant_helper<const char*>::set(&mStore, other.get<const char*>()); |
95 | | - return; |
96 | | - case VariantType::ArrayInt: |
97 | | - mSize = other.mSize; |
98 | | - variant_helper<int*>::set(&mStore, other.get<int*>(), mSize); |
99 | | - return; |
100 | | - case VariantType::ArrayFloat: |
101 | | - mSize = other.mSize; |
102 | | - variant_helper<float*>::set(&mStore, other.get<float*>(), mSize); |
103 | | - return; |
104 | | - case VariantType::ArrayDouble: |
105 | | - mSize = other.mSize; |
106 | | - variant_helper<double*>::set(&mStore, other.get<double*>(), mSize); |
107 | | - return; |
108 | | - case VariantType::ArrayBool: |
109 | | - mSize = other.mSize; |
110 | | - variant_helper<bool*>::set(&mStore, other.get<bool*>(), mSize); |
111 | | - return; |
| 92 | +/// Helper visitor for Variant |
| 93 | +template <typename F> |
| 94 | +bool visitStoredObject(VariantType type, F&& f) |
| 95 | +{ |
| 96 | + switch (type) { |
112 | 97 | case VariantType::ArrayString: |
113 | | - mSize = other.mSize; |
114 | | - variant_helper<std::vector<std::string>>::set(&mStore, other.get<std::vector<std::string>>()); |
115 | | - return; |
| 98 | + f.template operator()<std::vector<std::string>>(); |
| 99 | + return true; |
| 100 | + case VariantType::Array2DInt: |
| 101 | + f.template operator()<Array2D<int>>(); |
| 102 | + return true; |
| 103 | + case VariantType::Array2DFloat: |
| 104 | + f.template operator()<Array2D<float>>(); |
| 105 | + return true; |
| 106 | + case VariantType::Array2DDouble: |
| 107 | + f.template operator()<Array2D<double>>(); |
| 108 | + return true; |
| 109 | + case VariantType::LabeledArrayInt: |
| 110 | + f.template operator()<LabeledArray<int>>(); |
| 111 | + return true; |
| 112 | + case VariantType::LabeledArrayFloat: |
| 113 | + f.template operator()<LabeledArray<float>>(); |
| 114 | + return true; |
| 115 | + case VariantType::LabeledArrayDouble: |
| 116 | + f.template operator()<LabeledArray<double>>(); |
| 117 | + return true; |
| 118 | + case VariantType::LabeledArrayString: |
| 119 | + f.template operator()<LabeledArray<std::string>>(); |
| 120 | + return true; |
116 | 121 | default: |
117 | | - mStore = other.mStore; |
118 | | - mSize = other.mSize; |
| 122 | + return false; |
119 | 123 | } |
120 | 124 | } |
121 | 125 |
|
122 | | -Variant::Variant(Variant&& other) noexcept : mType(other.mType) |
| 126 | +/// Types for which the storage keeps a pointer to a manually allocated buffer |
| 127 | +bool holdsMallocedPointer(VariantType type) |
123 | 128 | { |
124 | | - mStore = other.mStore; |
125 | | - mSize = other.mSize; |
126 | | - switch (mType) { |
| 129 | + switch (type) { |
127 | 130 | case VariantType::String: |
128 | 131 | case VariantType::ArrayInt: |
129 | 132 | case VariantType::ArrayFloat: |
130 | 133 | case VariantType::ArrayDouble: |
131 | 134 | case VariantType::ArrayBool: |
132 | | - case VariantType::ArrayString: |
133 | | - *reinterpret_cast<void**>(&(other.mStore)) = nullptr; |
134 | | - return; |
| 135 | + return true; |
135 | 136 | default: |
136 | | - return; |
| 137 | + return false; |
137 | 138 | } |
138 | 139 | } |
139 | 140 |
|
140 | | -Variant::~Variant() |
| 141 | +template <typename T> |
| 142 | +T* copyBuffer(T const* values, size_t size) |
| 143 | +{ |
| 144 | + if (values == nullptr) { |
| 145 | + return nullptr; |
| 146 | + } |
| 147 | + return reinterpret_cast<T*>(std::memcpy(std::malloc(size * sizeof(T)), values, size * sizeof(T))); |
| 148 | +} |
| 149 | +} // namespace |
| 150 | + |
| 151 | +Variant::Variant(VariantType type) : mType{type} |
| 152 | +{ |
| 153 | + // Make sure that destroying a Variant created without a value is always safe |
| 154 | + // by creating a default stored object upfront |
| 155 | + if (!visitStoredObject(mType, [this]<typename T>() { new (&mStore) T{}; })) { |
| 156 | + std::memset(&mStore, 0, sizeof(mStore)); |
| 157 | + } |
| 158 | +} |
| 159 | + |
| 160 | +void Variant::copyStore(Variant const& other) |
141 | 161 | { |
142 | | - // In case we allocated an array, we |
143 | | - // should delete it. |
| 162 | + // Proper objects are simply copied |
| 163 | + if (visitStoredObject(mType, [this, &other]<typename T>() { new (&mStore) T(*reinterpret_cast<T const*>(&other.mStore)); })) { |
| 164 | + return; |
| 165 | + } |
| 166 | + // Manually allocated buffers have to be managed |
144 | 167 | switch (mType) { |
145 | | - case VariantType::String: |
| 168 | + case VariantType::String: { |
| 169 | + auto const* value = *reinterpret_cast<char const* const*>(&other.mStore); |
| 170 | + *reinterpret_cast<char**>(&mStore) = value != nullptr ? strdup(value) : nullptr; |
| 171 | + return; |
| 172 | + } |
146 | 173 | case VariantType::ArrayInt: |
| 174 | + *reinterpret_cast<int**>(&mStore) = copyBuffer(*reinterpret_cast<int* const*>(&other.mStore), mSize); |
| 175 | + return; |
147 | 176 | case VariantType::ArrayFloat: |
| 177 | + *reinterpret_cast<float**>(&mStore) = copyBuffer(*reinterpret_cast<float* const*>(&other.mStore), mSize); |
| 178 | + return; |
148 | 179 | case VariantType::ArrayDouble: |
149 | | - case VariantType::ArrayBool: { |
150 | | - if (reinterpret_cast<void**>(&mStore) != nullptr) { |
151 | | - free(*reinterpret_cast<void**>(&mStore)); |
152 | | - } |
| 180 | + *reinterpret_cast<double**>(&mStore) = copyBuffer(*reinterpret_cast<double* const*>(&other.mStore), mSize); |
153 | 181 | return; |
154 | | - } |
155 | | - case VariantType::ArrayString: { |
156 | | - // Allocated with placement new. Nothing to delete. |
| 182 | + case VariantType::ArrayBool: |
| 183 | + *reinterpret_cast<bool**>(&mStore) = copyBuffer(*reinterpret_cast<bool* const*>(&other.mStore), mSize); |
157 | 184 | return; |
158 | | - } |
159 | 185 | default: |
160 | | - return; |
| 186 | + // Trivially copyable content |
| 187 | + mStore = other.mStore; |
| 188 | + } |
| 189 | +} |
| 190 | + |
| 191 | +void Variant::moveStore(Variant& other) noexcept |
| 192 | +{ |
| 193 | + // Correct move for objects, leaving proper "moved from" state |
| 194 | + if (visitStoredObject(mType, [this, &other]<typename T>() { new (&mStore) T(std::move(*reinterpret_cast<T*>(&other.mStore))); })) { |
| 195 | + return; |
| 196 | + } |
| 197 | + mStore = other.mStore; |
| 198 | + // Buffers have to change their owner |
| 199 | + if (holdsMallocedPointer(mType)) { |
| 200 | + *reinterpret_cast<void**>(&other.mStore) = nullptr; |
| 201 | + } |
| 202 | +} |
| 203 | + |
| 204 | +void Variant::destroyStore() noexcept |
| 205 | +{ |
| 206 | + // destroy objects |
| 207 | + if (visitStoredObject(mType, [this]<typename T>() { std::destroy_at(reinterpret_cast<T*>(&mStore)); })) { |
| 208 | + return; |
| 209 | + } |
| 210 | + // deallocate buffers |
| 211 | + if (holdsMallocedPointer(mType)) { |
| 212 | + free(*reinterpret_cast<void**>(&mStore)); |
161 | 213 | } |
162 | 214 | } |
163 | 215 |
|
| 216 | +Variant::Variant(const Variant& other) : mType(other.mType), mSize(other.mSize) |
| 217 | +{ |
| 218 | + copyStore(other); |
| 219 | +} |
| 220 | + |
| 221 | +Variant::Variant(Variant&& other) noexcept : mType(other.mType), mSize(other.mSize) |
| 222 | +{ |
| 223 | + moveStore(other); |
| 224 | +} |
| 225 | + |
| 226 | +Variant::~Variant() |
| 227 | +{ |
| 228 | + destroyStore(); |
| 229 | +} |
| 230 | + |
164 | 231 | Variant& Variant::operator=(const Variant& other) |
165 | 232 | { |
166 | | - mSize = other.mSize; |
167 | | - mType = other.mType; |
168 | | - switch (mType) { |
169 | | - case VariantType::String: |
170 | | - variant_helper<const char*>::set(&mStore, other.get<const char*>()); |
171 | | - return *this; |
172 | | - case VariantType::ArrayInt: |
173 | | - variant_helper<int*>::set(&mStore, other.get<int*>(), mSize); |
174 | | - return *this; |
175 | | - case VariantType::ArrayFloat: |
176 | | - variant_helper<float*>::set(&mStore, other.get<float*>(), mSize); |
177 | | - return *this; |
178 | | - case VariantType::ArrayDouble: |
179 | | - variant_helper<double*>::set(&mStore, other.get<double*>(), mSize); |
180 | | - return *this; |
181 | | - case VariantType::ArrayBool: |
182 | | - variant_helper<bool*>::set(&mStore, other.get<bool*>(), mSize); |
183 | | - return *this; |
184 | | - case VariantType::ArrayString: |
185 | | - variant_helper<std::vector<std::string>>::set(&mStore, other.get<std::vector<std::string>>()); |
186 | | - return *this; |
187 | | - default: |
188 | | - mStore = other.mStore; |
189 | | - return *this; |
| 233 | + if (this != &other) { |
| 234 | + // Copy first, so that a throwing copy leaves this Variant untouched |
| 235 | + Variant copy(other); |
| 236 | + *this = std::move(copy); |
190 | 237 | } |
| 238 | + return *this; |
191 | 239 | } |
192 | 240 |
|
193 | 241 | Variant& Variant::operator=(Variant&& other) noexcept |
194 | 242 | { |
195 | | - mSize = other.mSize; |
196 | | - mType = other.mType; |
197 | | - switch (mType) { |
198 | | - case VariantType::String: |
199 | | - variant_helper<const char*>::set(&mStore, other.get<const char*>()); |
200 | | - *reinterpret_cast<char**>(&(other.mStore)) = nullptr; |
201 | | - return *this; |
202 | | - case VariantType::ArrayInt: |
203 | | - variant_helper<int*>::set(&mStore, other.get<int*>(), mSize); |
204 | | - *reinterpret_cast<int**>(&(other.mStore)) = nullptr; |
205 | | - return *this; |
206 | | - case VariantType::ArrayFloat: |
207 | | - variant_helper<float*>::set(&mStore, other.get<float*>(), mSize); |
208 | | - *reinterpret_cast<float**>(&(other.mStore)) = nullptr; |
209 | | - return *this; |
210 | | - case VariantType::ArrayDouble: |
211 | | - variant_helper<double*>::set(&mStore, other.get<double*>(), mSize); |
212 | | - *reinterpret_cast<double**>(&(other.mStore)) = nullptr; |
213 | | - return *this; |
214 | | - case VariantType::ArrayBool: |
215 | | - variant_helper<bool*>::set(&mStore, other.get<bool*>(), mSize); |
216 | | - *reinterpret_cast<bool**>(&(other.mStore)) = nullptr; |
217 | | - return *this; |
218 | | - case VariantType::ArrayString: |
219 | | - variant_helper<std::vector<std::string>>::set(&mStore, other.get<std::vector<std::string>>()); |
220 | | - *reinterpret_cast<std::vector<std::string>**>(&(other.mStore)) = nullptr; |
221 | | - return *this; |
222 | | - default: |
223 | | - mStore = other.mStore; |
224 | | - return *this; |
| 243 | + if (this != &other) { |
| 244 | + destroyStore(); |
| 245 | + mType = other.mType; |
| 246 | + mSize = other.mSize; |
| 247 | + moveStore(other); |
225 | 248 | } |
| 249 | + return *this; |
226 | 250 | } |
227 | 251 |
|
228 | 252 | std::pair<std::vector<std::string>, std::vector<std::string>> extractLabels(boost::property_tree::ptree const& tree) |
|
0 commit comments