openPMD-api
 
Loading...
Searching...
No Matches
Attribute.hpp
1/* Copyright 2017-2025 Fabian Koller, Axel Huebl, Franz Poeschel
2 *
3 * This file is part of openPMD-api.
4 *
5 * openPMD-api is free software: you can redistribute it and/or modify
6 * it under the terms of of either the GNU General Public License or
7 * the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * openPMD-api is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License and the GNU Lesser General Public License
15 * for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * and the GNU Lesser General Public License along with openPMD-api.
19 * If not, see <http://www.gnu.org/licenses/>.
20 */
21#pragma once
22
23#include "openPMD/Datatype.hpp"
24#include "openPMD/auxiliary/TypeTraits.hpp"
25#include "openPMD/auxiliary/Variant.hpp"
26
27// comment to prevent clang-format from moving this #include up
28// datatype macros may be included and un-included in other headers
29#include "openPMD/DatatypeMacros.hpp"
30
31#include <algorithm>
32#include <array>
33#include <complex>
34#include <cstdint>
35#include <iterator>
36#include <optional>
37#include <stdexcept>
38#include <string>
39#include <type_traits>
40#include <utility>
41#include <variant>
42#include <vector>
43
44namespace openPMD
45{
46// TODO This might have to be a Writable
47// Reasoning - Flushes are expected to be done often.
48// Attributes should not be written unless dirty.
49// At the moment the dirty check is done at Attributable level,
50// resulting in all of an Attributables Attributes being written to disk even if
51// only one changes
55
56#define OPENPMD_ENUMERATE_TYPES(type) , type
57
58class Attribute
59 : public auxiliary::Variant<Datatype OPENPMD_FOREACH_DATATYPE(
60 OPENPMD_ENUMERATE_TYPES)>
61
62#undef OPENPMD_ENUMERATE_TYPES
63
64{
65public:
67 {};
68 static constexpr from_any_tag from_any = from_any_tag{};
69
79
80#define OPENPMD_ATTRIBUTE_CONSTRUCTOR_FROM_VARIANT(TYPE) \
81 Attribute(TYPE val) : Variant(Variant::from_basic_type, std::move(val)) \
82 {}
83
84 OPENPMD_FOREACH_DATATYPE(OPENPMD_ATTRIBUTE_CONSTRUCTOR_FROM_VARIANT)
85
86#undef OPENPMD_ATTRIBUTE_CONSTRUCTOR_FROM_VARIANT
87
88 Attribute(from_any_tag, std::any val)
89 : Variant(Variant::from_any, std::move(val))
90 {}
91
101 template <typename U>
102 U get() const;
103
115 template <typename U>
116 std::optional<U> getOptional() const;
117
118private:
119 template <typename U>
120 std::variant<U, std::runtime_error> get_impl() const;
121};
122
123namespace detail
124{
125 template <typename T, typename U>
126 auto doConvert(T const *pv) -> std::variant<U, std::runtime_error>
127 {
128 (void)pv;
129 if constexpr (std::is_convertible_v<T, U>)
130 {
131 return {static_cast<U>(*pv)};
132 }
133 else if constexpr (
134 std::is_same_v<T, std::string> && auxiliary::IsChar_v<U>)
135 {
136 if (pv->size() == 1)
137 {
138 return static_cast<U>(pv->at(0));
139 }
140 else
141 {
142 return {std::runtime_error(
143 "getCast: cast from string to char only "
144 "possible if string has length 1.")};
145 }
146 }
147 else if constexpr (
148 auxiliary::IsChar_v<T> && std::is_same_v<U, std::string>)
149 {
150 return std::string(1, *pv);
151 }
152 else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsVector_v<U>)
153 {
154 U res{};
155 res.reserve(pv->size());
156 if constexpr (std::is_convertible_v<
157 typename T::value_type,
158 typename U::value_type>)
159 {
160 std::copy(pv->begin(), pv->end(), std::back_inserter(res));
161 return {res};
162 }
163 else
164 {
165 // try a dynamic conversion recursively
166 for (auto const &val : *pv)
167 {
168 auto conv = doConvert<
169 typename T::value_type,
170 typename U::value_type>(&val);
171 if (auto conv_val =
172 std::get_if<typename U::value_type>(&conv);
173 conv_val)
174 {
175 res.push_back(std::move(*conv_val));
176 }
177 else
178 {
179 auto exception = std::get<std::runtime_error>(conv);
180 return {std::runtime_error(
181 std::string(
182 "getCast: no vector cast possible, recursive "
183 "error: ") +
184 exception.what())};
185 }
186 }
187 return {res};
188 }
189 }
190 // conversion cast: array to vector
191 // if a backend reports a std::array<> for something where
192 // the frontend expects a vector
193 else if constexpr (auxiliary::IsArray_v<T> && auxiliary::IsVector_v<U>)
194 {
195 U res{};
196 res.reserve(pv->size());
197 if constexpr (std::is_convertible_v<
198 typename T::value_type,
199 typename U::value_type>)
200 {
201 std::copy(pv->begin(), pv->end(), std::back_inserter(res));
202 return {res};
203 }
204 else
205 {
206 // try a dynamic conversion recursively
207 for (auto const &val : *pv)
208 {
209 auto conv = doConvert<
210 typename T::value_type,
211 typename U::value_type>(&val);
212 if (auto conv_val =
213 std::get_if<typename U::value_type>(&conv);
214 conv_val)
215 {
216 res.push_back(std::move(*conv_val));
217 }
218 else
219 {
220 auto exception = std::get<std::runtime_error>(conv);
221 return {std::runtime_error(
222 std::string(
223 "getCast: no array to vector conversion "
224 "possible, recursive error: ") +
225 exception.what())};
226 }
227 }
228 return {res};
229 }
230 }
231 // conversion cast: vector to array
232 // if a backend reports a std::vector<> for something where
233 // the frontend expects an array
234 else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsArray_v<U>)
235 {
236 U res{};
237 if constexpr (std::is_convertible_v<
238 typename T::value_type,
239 typename U::value_type>)
240 {
241 if (res.size() != pv->size())
242 {
243 return std::runtime_error(
244 "getCast: no vector to array conversion possible "
245 "(wrong "
246 "requested array size).");
247 }
248 for (size_t i = 0; i < res.size(); ++i)
249 {
250 res[i] = static_cast<typename U::value_type>((*pv)[i]);
251 }
252 return {res};
253 }
254 else
255 {
256 // try a dynamic conversion recursively
257 for (size_t i = 0; i <= res.size(); ++i)
258 {
259 auto const &val = (*pv)[i];
260 auto conv = doConvert<
261 typename T::value_type,
262 typename U::value_type>(&val);
263 if (auto conv_val =
264 std::get_if<typename U::value_type>(&conv);
265 conv_val)
266 {
267 res[i] = std::move(*conv_val);
268 }
269 else
270 {
271 auto exception = std::get<std::runtime_error>(conv);
272 return {std::runtime_error(
273 std::string(
274 "getCast: no vector to array conversion "
275 "possible, recursive error: ") +
276 exception.what())};
277 }
278 }
279 return {res};
280 }
281 }
282 // conversion cast: turn a single value into a 1-element vector
283 else if constexpr (auxiliary::IsVector_v<U>)
284 {
285 U res{};
286 res.reserve(1);
287 if constexpr (std::is_convertible_v<T, typename U::value_type>)
288 {
289 res.push_back(static_cast<typename U::value_type>(*pv));
290 return {res};
291 }
292 else
293 {
294 // try a dynamic conversion recursively
295 auto conv = doConvert<T, typename U::value_type>(pv);
296 if (auto conv_val = std::get_if<typename U::value_type>(&conv);
297 conv_val)
298 {
299 res.push_back(std::move(*conv_val));
300 return {res};
301 }
302 else
303 {
304 auto exception = std::get<std::runtime_error>(conv);
305 return {std::runtime_error(
306 std::string(
307 "getCast: no scalar to vector conversion "
308 "possible, recursive error: ") +
309 exception.what())};
310 }
311 }
312 }
313 else
314 {
315 return {std::runtime_error("getCast: no cast possible.")};
316 }
317#if defined(__INTEL_COMPILER)
318/*
319 * ICPC has trouble with if constexpr, thinking that return statements are
320 * missing afterwards. Deactivate the warning.
321 * Note that putting a statement here will not help to fix this since it will
322 * then complain about unreachable code.
323 * https://community.intel.com/t5/Intel-C-Compiler/quot-if-constexpr-quot-and-quot-missing-return-statement-quot-in/td-p/1154551
324 */
325#pragma warning(disable : 1011)
326 }
327#pragma warning(default : 1011)
328#else
329 }
330#endif
331
332 template <typename T, typename U>
333 auto doConvertOptional(T const *pv) -> std::optional<U>
334 {
335 auto eitherValueOrError = doConvert<T, U>(pv);
336 return std::visit(
337 [](auto &containedValue) -> std::optional<U> {
338 using Res = std::decay_t<decltype(containedValue)>;
339 if constexpr (std::is_same_v<Res, std::runtime_error>)
340 {
341 return std::nullopt;
342 }
343 else
344 {
345 return {std::move(containedValue)};
346 }
347 },
348 eitherValueOrError);
349 }
350} // namespace detail
351} // namespace openPMD
352
353#include "openPMD/UndefDatatypeMacros.hpp"
Definition Attribute.hpp:64
U get() const
Retrieve a stored specific Attribute and cast if convertible.
Definition Attribute.cpp:105
std::optional< U > getOptional() const
Retrieve a stored specific Attribute and cast if convertible.
Definition Attribute.cpp:125
Generic object to store a set of datatypes in without losing type safety.
Definition Variant.hpp:40
Public definitions of openPMD-api.
Definition Date.cpp:29
@ T
time
Definition UnitDimension.hpp:41
STL namespace.
Definition Attribute.hpp:67