openPMD-api
Attribute.hpp
1 /* Copyright 2017-2021 Fabian Koller
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 #include <algorithm>
28 #include <array>
29 #include <complex>
30 #include <cstdint>
31 #include <iterator>
32 #include <optional>
33 #include <stdexcept>
34 #include <string>
35 #include <type_traits>
36 #include <utility>
37 #include <variant>
38 #include <vector>
39 
40 namespace openPMD
41 {
42 // TODO This might have to be a Writable
43 // Reasoning - Flushes are expected to be done often.
44 // Attributes should not be written unless dirty.
45 // At the moment the dirty check is done at Attributable level,
46 // resulting in all of an Attributables Attributes being written to disk even if
47 // only one changes
54 class Attribute
55  : public auxiliary::Variant<
56  Datatype,
57  char,
58  unsigned char,
59  signed char,
60  short,
61  int,
62  long,
63  long long,
64  unsigned short,
65  unsigned int,
66  unsigned long,
67  unsigned long long,
68  float,
69  double,
70  long double,
71  std::complex<float>,
72  std::complex<double>,
73  std::complex<long double>,
74  std::string,
75  std::vector<char>,
76  std::vector<short>,
77  std::vector<int>,
78  std::vector<long>,
79  std::vector<long long>,
80  std::vector<unsigned char>,
81  std::vector<unsigned short>,
82  std::vector<unsigned int>,
83  std::vector<unsigned long>,
84  std::vector<unsigned long long>,
85  std::vector<float>,
86  std::vector<double>,
87  std::vector<long double>,
88  std::vector<std::complex<float> >,
89  std::vector<std::complex<double> >,
90  std::vector<std::complex<long double> >,
91  std::vector<signed char>,
92  std::vector<std::string>,
93  std::array<double, 7>,
94  bool>
95 {
96 public:
97  Attribute(resource r) : Variant(std::move(r))
98  {}
99 
109  template <typename T>
110  Attribute(T &&val) : Variant(resource(std::forward<T>(val)))
111  {}
112 
122  template <typename U>
123  U get() const;
124 
136  template <typename U>
137  std::optional<U> getOptional() const;
138 };
139 
140 namespace detail
141 {
142  template <typename T, typename U>
143  auto doConvert(T *pv) -> std::variant<U, std::runtime_error>
144  {
145  (void)pv;
146  if constexpr (std::is_convertible_v<T, U>)
147  {
148  return {static_cast<U>(*pv)};
149  }
150  else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsVector_v<U>)
151  {
152  if constexpr (std::is_convertible_v<
153  typename T::value_type,
154  typename U::value_type>)
155  {
156  U res{};
157  res.reserve(pv->size());
158  std::copy(pv->begin(), pv->end(), std::back_inserter(res));
159  return {res};
160  }
161  else
162  {
163  return {
164  std::runtime_error("getCast: no vector cast possible.")};
165  }
166  }
167  // conversion cast: array to vector
168  // if a backend reports a std::array<> for something where
169  // the frontend expects a vector
170  else if constexpr (auxiliary::IsArray_v<T> && auxiliary::IsVector_v<U>)
171  {
172  if constexpr (std::is_convertible_v<
173  typename T::value_type,
174  typename U::value_type>)
175  {
176  U res{};
177  res.reserve(pv->size());
178  std::copy(pv->begin(), pv->end(), std::back_inserter(res));
179  return {res};
180  }
181  else
182  {
183  return {std::runtime_error(
184  "getCast: no array to vector conversion possible.")};
185  }
186  }
187  // conversion cast: vector to array
188  // if a backend reports a std::vector<> for something where
189  // the frontend expects an array
190  else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsArray_v<U>)
191  {
192  if constexpr (std::is_convertible_v<
193  typename T::value_type,
194  typename U::value_type>)
195  {
196  U res{};
197  if (res.size() != pv->size())
198  {
199  return std::runtime_error(
200  "getCast: no vector to array conversion possible "
201  "(wrong "
202  "requested array size).");
203  }
204  for (size_t i = 0; i < res.size(); ++i)
205  {
206  res[i] = static_cast<typename U::value_type>((*pv)[i]);
207  }
208  return {res};
209  }
210  else
211  {
212  return {std::runtime_error(
213  "getCast: no vector to array conversion possible.")};
214  }
215  }
216  // conversion cast: turn a single value into a 1-element vector
217  else if constexpr (auxiliary::IsVector_v<U>)
218  {
219  if constexpr (std::is_convertible_v<T, typename U::value_type>)
220  {
221  U res{};
222  res.reserve(1);
223  res.push_back(static_cast<typename U::value_type>(*pv));
224  return {res};
225  }
226  else
227  {
228  return {std::runtime_error(
229  "getCast: no scalar to vector conversion possible.")};
230  }
231  }
232  else
233  {
234  return {std::runtime_error("getCast: no cast possible.")};
235  }
236 #if defined(__INTEL_COMPILER)
237 /*
238  * ICPC has trouble with if constexpr, thinking that return statements are
239  * missing afterwards. Deactivate the warning.
240  * Note that putting a statement here will not help to fix this since it will
241  * then complain about unreachable code.
242  * https://community.intel.com/t5/Intel-C-Compiler/quot-if-constexpr-quot-and-quot-missing-return-statement-quot-in/td-p/1154551
243  */
244 #pragma warning(disable : 1011)
245  }
246 #pragma warning(default : 1011)
247 #else
248  }
249 #endif
250 } // namespace detail
251 
252 template <typename U>
253 U Attribute::get() const
254 {
255  auto eitherValueOrError = std::visit(
256  [](auto &&containedValue) -> std::variant<U, std::runtime_error> {
257  using containedType = std::decay_t<decltype(containedValue)>;
258  return detail::doConvert<containedType, U>(&containedValue);
259  },
260  Variant::getResource());
261  return std::visit(
262  [](auto &&containedValue) -> U {
263  using T = std::decay_t<decltype(containedValue)>;
264  if constexpr (std::is_same_v<T, std::runtime_error>)
265  {
266  throw std::move(containedValue);
267  }
268  else
269  {
270  return std::move(containedValue);
271  }
272  },
273  std::move(eitherValueOrError));
274 }
275 
276 template <typename U>
277 std::optional<U> Attribute::getOptional() const
278 {
279  auto eitherValueOrError = std::visit(
280  [](auto &&containedValue) -> std::variant<U, std::runtime_error> {
281  using containedType = std::decay_t<decltype(containedValue)>;
282  return detail::doConvert<containedType, U>(&containedValue);
283  },
284  Variant::getResource());
285  return std::visit(
286  [](auto &&containedValue) -> std::optional<U> {
287  using T = std::decay_t<decltype(containedValue)>;
288  if constexpr (std::is_same_v<T, std::runtime_error>)
289  {
290  return std::nullopt;
291  }
292  else
293  {
294  return {std::move(containedValue)};
295  }
296  },
297  std::move(eitherValueOrError));
298 }
299 } // namespace openPMD
U get() const
Retrieve a stored specific Attribute and cast if convertible.
Definition: Attribute.hpp:253
STL namespace.
Generic object to store a set of datatypes in without losing type safety.
Definition: Variant.hpp:39
std::optional< U > getOptional() const
Retrieve a stored specific Attribute and cast if convertible.
Definition: Attribute.hpp:277
Varidic datatype supporting at least all formats for attributes specified in the openPMD standard...
Definition: Attribute.hpp:54
Definition: Container.cpp:51
Public definitions of openPMD-api.
Attribute(T &&val)
Compiler bug: CUDA (nvcc) releases 11.0.3 (v11.0.221), 11.1 (v11.1.105): no instance of constructor "...
Definition: Attribute.hpp:110