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/Variant.hpp"
25 
26 #include <algorithm>
27 #include <array>
28 #include <complex>
29 #include <cstdint>
30 #include <iterator>
31 #include <stdexcept>
32 #include <string>
33 #include <type_traits>
34 #include <utility>
35 #include <vector>
36 
37 namespace openPMD
38 {
39 // TODO This might have to be a Writable
40 // Reasoning - Flushes are expected to be done often.
41 // Attributes should not be written unless dirty.
42 // At the moment the dirty check is done at Attributable level,
43 // resulting in all of an Attributables Attributes being written to disk even if
44 // only one changes
51 class Attribute
52  : public auxiliary::Variant<
53  Datatype,
54  char,
55  unsigned char, // signed char,
56  short,
57  int,
58  long,
59  long long,
60  unsigned short,
61  unsigned int,
62  unsigned long,
63  unsigned long long,
64  float,
65  double,
66  long double,
67  std::complex<float>,
68  std::complex<double>,
69  std::complex<long double>,
70  std::string,
71  std::vector<char>,
72  std::vector<short>,
73  std::vector<int>,
74  std::vector<long>,
75  std::vector<long long>,
76  std::vector<unsigned char>,
77  std::vector<unsigned short>,
78  std::vector<unsigned int>,
79  std::vector<unsigned long>,
80  std::vector<unsigned long long>,
81  std::vector<float>,
82  std::vector<double>,
83  std::vector<long double>,
84  std::vector<std::complex<float> >,
85  std::vector<std::complex<double> >,
86  std::vector<std::complex<long double> >,
87  std::vector<std::string>,
88  std::array<double, 7>,
89  bool>
90 {
91 public:
92  Attribute(resource r) : Variant(std::move(r))
93  {}
94 
104  template <typename U>
105  U get() const;
106 };
107 
108 template <
109  typename T,
110  typename U,
111  bool isConvertible = std::is_convertible<T, U>::value>
112 struct DoConvert;
113 
114 template <typename T, typename U>
115 struct DoConvert<T, U, false>
116 {
117  U operator()(T *)
118  {
119  throw std::runtime_error("getCast: no cast possible.");
120  }
121 };
122 
123 template <typename T, typename U>
124 struct DoConvert<T, U, true>
125 {
126  U operator()(T *pv)
127  {
128  return static_cast<U>(*pv);
129  }
130 };
131 
132 template <typename T, typename U>
133 struct DoConvert<std::vector<T>, std::vector<U>, false>
134 {
135  static constexpr bool convertible = std::is_convertible<T, U>::value;
136 
137  template <typename UU = U>
138  auto operator()(std::vector<T> const *pv) ->
139  typename std::enable_if<convertible, std::vector<UU> >::type
140  {
141  std::vector<U> u;
142  u.reserve(pv->size());
143  std::copy(pv->begin(), pv->end(), std::back_inserter(u));
144  return u;
145  }
146 
147  template <typename UU = U>
148  auto operator()(std::vector<T> const *) ->
149  typename std::enable_if<!convertible, std::vector<UU> >::type
150  {
151  throw std::runtime_error("getCast: no vector cast possible.");
152  }
153 };
154 
155 // conversion cast: turn a single value into a 1-element vector
156 template <typename T, typename U>
157 struct DoConvert<T, std::vector<U>, false>
158 {
159  static constexpr bool convertible = std::is_convertible<T, U>::value;
160 
161  template <typename UU = U>
162  auto operator()(T const *pv) ->
163  typename std::enable_if<convertible, std::vector<UU> >::type
164  {
165  std::vector<U> u;
166  u.reserve(1);
167  u.push_back(static_cast<U>(*pv));
168  return u;
169  }
170 
171  template <typename UU = U>
172  auto operator()(T const *) ->
173  typename std::enable_if<!convertible, std::vector<UU> >::type
174  {
175  throw std::runtime_error(
176  "getCast: no scalar to vector conversion possible.");
177  }
178 };
179 
180 // conversion cast: array to vector
181 // if a backend reports a std::array<> for something where the frontend expects
182 // a vector
183 template <typename T, typename U, size_t n>
184 struct DoConvert<std::array<T, n>, std::vector<U>, false>
185 {
186  static constexpr bool convertible = std::is_convertible<T, U>::value;
187 
188  template <typename UU = U>
189  auto operator()(std::array<T, n> const *pv) ->
190  typename std::enable_if<convertible, std::vector<UU> >::type
191  {
192  std::vector<U> u;
193  u.reserve(n);
194  std::copy(pv->begin(), pv->end(), std::back_inserter(u));
195  return u;
196  }
197 
198  template <typename UU = U>
199  auto operator()(std::array<T, n> const *) ->
200  typename std::enable_if<!convertible, std::vector<UU> >::type
201  {
202  throw std::runtime_error(
203  "getCast: no array to vector conversion possible.");
204  }
205 };
206 
207 // conversion cast: vector to array
208 // if a backend reports a std::vector<> for something where the frontend expects
209 // an array
210 template <typename T, typename U, size_t n>
211 struct DoConvert<std::vector<T>, std::array<U, n>, false>
212 {
213  static constexpr bool convertible = std::is_convertible<T, U>::value;
214 
215  template <typename UU = U>
216  auto operator()(std::vector<T> const *pv) ->
217  typename std::enable_if<convertible, std::array<UU, n> >::type
218  {
219  std::array<U, n> u;
220  if (n != pv->size())
221  {
222  throw std::runtime_error(
223  "getCast: no vector to array conversion possible "
224  "(wrong requested array size).");
225  }
226  for (size_t i = 0; i < n; ++i)
227  {
228  u[i] = static_cast<U>((*pv)[i]);
229  }
230  return u;
231  }
232 
233  template <typename UU = U>
234  auto operator()(std::vector<T> const *) ->
235  typename std::enable_if<!convertible, std::array<UU, n> >::type
236  {
237  throw std::runtime_error(
238  "getCast: no vector to array conversion possible.");
239  }
240 };
241 
248 template <typename U>
249 inline U getCast(Attribute const &a)
250 {
251  auto v = a.getResource();
252 
253  // icpc 2021.3.0 does not like variantSrc::visit (with mpark-variant)
254  // we use variantSrc::visit for the other compilers to avoid having an
255  // endless list of if-then-else
256  // also, once we switch to C++17, we might throw this out in
257  // favor of a hopefully working std::visit
258 #if defined(__ICC) || defined(__INTEL_COMPILER)
259  if (auto pvalue_c = variantSrc::get_if<char>(&v))
260  return DoConvert<char, U>{}(pvalue_c);
261  else if (auto pvalue_uc = variantSrc::get_if<unsigned char>(&v))
262  return DoConvert<unsigned char, U>{}(pvalue_uc);
263  else if (auto pvalue_s = variantSrc::get_if<short>(&v))
264  return DoConvert<short, U>{}(pvalue_s);
265  else if (auto pvalue_i = variantSrc::get_if<int>(&v))
266  return DoConvert<int, U>{}(pvalue_i);
267  else if (auto pvalue_l = variantSrc::get_if<long>(&v))
268  return DoConvert<long, U>{}(pvalue_l);
269  else if (auto pvalue_ll = variantSrc::get_if<long long>(&v))
270  return DoConvert<long long, U>{}(pvalue_ll);
271  else if (auto pvalue_us = variantSrc::get_if<unsigned short>(&v))
272  return DoConvert<unsigned short, U>{}(pvalue_us);
273  else if (auto pvalue_ui = variantSrc::get_if<unsigned int>(&v))
274  return DoConvert<unsigned int, U>{}(pvalue_ui);
275  else if (auto pvalue_ul = variantSrc::get_if<unsigned long>(&v))
276  return DoConvert<unsigned long, U>{}(pvalue_ul);
277  else if (auto pvalue_ull = variantSrc::get_if<unsigned long long>(&v))
278  return DoConvert<unsigned long long, U>{}(pvalue_ull);
279  else if (auto pvalue_f = variantSrc::get_if<float>(&v))
280  return DoConvert<float, U>{}(pvalue_f);
281  else if (auto pvalue_d = variantSrc::get_if<double>(&v))
282  return DoConvert<double, U>{}(pvalue_d);
283  else if (auto pvalue_ld = variantSrc::get_if<long double>(&v))
284  return DoConvert<long double, U>{}(pvalue_ld);
285  else if (auto pvalue_cf = variantSrc::get_if<std::complex<float> >(&v))
286  return DoConvert<std::complex<float>, U>{}(pvalue_cf);
287  else if (auto pvalue_cd = variantSrc::get_if<std::complex<double> >(&v))
288  return DoConvert<std::complex<double>, U>{}(pvalue_cd);
289  else if (
290  auto pvalue_cld = variantSrc::get_if<std::complex<long double> >(&v))
291  return DoConvert<std::complex<long double>, U>{}(pvalue_cld);
292  else if (auto pvalue_str = variantSrc::get_if<std::string>(&v))
293  return DoConvert<std::string, U>{}(pvalue_str);
294  // vector
295  else if (auto pvalue_vc = variantSrc::get_if<std::vector<char> >(&v))
296  return DoConvert<std::vector<char>, U>{}(pvalue_vc);
297  else if (
298  auto pvalue_vuc = variantSrc::get_if<std::vector<unsigned char> >(&v))
299  return DoConvert<std::vector<unsigned char>, U>{}(pvalue_vuc);
300  else if (auto pvalue_vs = variantSrc::get_if<std::vector<short> >(&v))
301  return DoConvert<std::vector<short>, U>{}(pvalue_vs);
302  else if (auto pvalue_vi = variantSrc::get_if<std::vector<int> >(&v))
303  return DoConvert<std::vector<int>, U>{}(pvalue_vi);
304  else if (auto pvalue_vl = variantSrc::get_if<std::vector<long> >(&v))
305  return DoConvert<std::vector<long>, U>{}(pvalue_vl);
306  else if (auto pvalue_vll = variantSrc::get_if<std::vector<long long> >(&v))
307  return DoConvert<std::vector<long long>, U>{}(pvalue_vll);
308  else if (
309  auto pvalue_vus = variantSrc::get_if<std::vector<unsigned short> >(&v))
310  return DoConvert<std::vector<unsigned short>, U>{}(pvalue_vus);
311  else if (
312  auto pvalue_vui = variantSrc::get_if<std::vector<unsigned int> >(&v))
313  return DoConvert<std::vector<unsigned int>, U>{}(pvalue_vui);
314  else if (
315  auto pvalue_vul = variantSrc::get_if<std::vector<unsigned long> >(&v))
316  return DoConvert<std::vector<unsigned long>, U>{}(pvalue_vul);
317  else if (
318  auto pvalue_vull =
319  variantSrc::get_if<std::vector<unsigned long long> >(&v))
320  return DoConvert<std::vector<unsigned long long>, U>{}(pvalue_vull);
321  else if (auto pvalue_vf = variantSrc::get_if<std::vector<float> >(&v))
322  return DoConvert<std::vector<float>, U>{}(pvalue_vf);
323  else if (auto pvalue_vd = variantSrc::get_if<std::vector<double> >(&v))
324  return DoConvert<std::vector<double>, U>{}(pvalue_vd);
325  else if (
326  auto pvalue_vld = variantSrc::get_if<std::vector<long double> >(&v))
327  return DoConvert<std::vector<long double>, U>{}(pvalue_vld);
328  else if (
329  auto pvalue_vcf =
330  variantSrc::get_if<std::vector<std::complex<float> > >(&v))
331  return DoConvert<std::vector<std::complex<float> >, U>{}(pvalue_vcf);
332  else if (
333  auto pvalue_vcd =
334  variantSrc::get_if<std::vector<std::complex<double> > >(&v))
335  return DoConvert<std::vector<std::complex<double> >, U>{}(pvalue_vcd);
336  else if (
337  auto pvalue_vcld =
338  variantSrc::get_if<std::vector<std::complex<long double> > >(&v))
339  return DoConvert<std::vector<std::complex<long double> >, U>{}(
340  pvalue_vcld);
341  else if (
342  auto pvalue_vstr = variantSrc::get_if<std::vector<std::string> >(&v))
343  return DoConvert<std::vector<std::string>, U>{}(pvalue_vstr);
344  // extra
345  else if (auto pvalue_vad = variantSrc::get_if<std::array<double, 7> >(&v))
346  return DoConvert<std::array<double, 7>, U>{}(pvalue_vad);
347  else if (auto pvalue_b = variantSrc::get_if<bool>(&v))
348  return DoConvert<bool, U>{}(pvalue_b);
349  else
350  throw std::runtime_error("getCast: unknown Datatype.");
351 
352 #else
353  return variantSrc::visit(
354  [](auto &&containedValue) -> U {
355  using containedType = std::decay_t<decltype(containedValue)>;
356  return DoConvert<containedType, U>{}(&containedValue);
357  },
358  v);
359 #endif
360 }
361 
362 template <typename U>
363 U Attribute::get() const
364 {
365  return getCast<U>(Variant::getResource());
366 }
367 
368 } // namespace openPMD
U get() const
Retrieve a stored specific Attribute and cast if convertible.
Definition: Attribute.hpp:363
Definition: Attribute.hpp:112
STL namespace.
Generic object to store a set of datatypes in without losing type safety.
Definition: Variant.hpp:40
U getCast(Attribute const &a)
Retrieve a stored specific Attribute and cast if convertible.
Definition: Attribute.hpp:249
Varidic datatype supporting at least all formats for attributes specified in the openPMD standard...
Definition: Attribute.hpp:51
Public definitions of openPMD-api.
Definition: Date.cpp:28
resource getResource() const
Retrieve the stored generic object.
Definition: Variant.hpp:75