openPMD-api
Datatype.hpp
1 /* Copyright 2017-2021 Fabian Koller, Franz Poeschel, Axel Huebl
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/auxiliary/TypeTraits.hpp"
24 #include "openPMD/auxiliary/UniquePtr.hpp"
25 
26 // comment to prevent clang-format from moving this #include up
27 // datatype macros may be included and un-included in other headers
28 #include "openPMD/DatatypeMacros.hpp"
29 
30 #include <array>
31 #include <climits>
32 #include <complex>
33 #include <cstdint>
34 #include <iosfwd>
35 #include <map>
36 #include <memory>
37 #include <stdexcept>
38 #include <string>
39 #include <tuple>
40 #include <type_traits>
41 #include <utility> // std::declval
42 #include <variant>
43 #include <vector>
44 
45 namespace openPMD
46 {
47 
50 enum class Datatype : int
51 {
52  CHAR,
53  UCHAR,
54  SCHAR,
55  SHORT,
56  INT,
57  LONG,
58  LONGLONG,
59  USHORT,
60  UINT,
61  ULONG,
62  ULONGLONG,
63  FLOAT,
64  DOUBLE,
65  LONG_DOUBLE,
66  CFLOAT,
67  CDOUBLE,
68  CLONG_DOUBLE,
69  STRING,
70  VEC_CHAR,
71  VEC_SHORT,
72  VEC_INT,
73  VEC_LONG,
74  VEC_LONGLONG,
75  VEC_UCHAR,
76  VEC_USHORT,
77  VEC_UINT,
78  VEC_ULONG,
79  VEC_ULONGLONG,
80  VEC_FLOAT,
81  VEC_DOUBLE,
82  VEC_LONG_DOUBLE,
83  VEC_CFLOAT,
84  VEC_CDOUBLE,
85  VEC_CLONG_DOUBLE,
86  VEC_SCHAR,
87  VEC_STRING,
88  ARR_DBL_7,
89 
90  BOOL,
91 
92  UNDEFINED
93 }; // Datatype
94 
100 std::vector<Datatype> openPMD_Datatypes();
101 
102 namespace detail
103 {
104  struct bottom
105  {};
106 
107  // std::variant, but ignore first template parameter
108  // little trick to avoid trailing commas in the macro expansions below
109  template <typename Arg, typename... Args>
110  using variant_tail_t = std::variant<Args...>;
111 } // namespace detail
112 
113 #define OPENPMD_ENUMERATE_TYPES(type) , type
114 
115 using dataset_types =
116  detail::variant_tail_t<detail::bottom OPENPMD_FOREACH_DATASET_DATATYPE(
117  OPENPMD_ENUMERATE_TYPES)>;
118 
119 using non_vector_types =
120  detail::variant_tail_t<detail::bottom OPENPMD_FOREACH_NONVECTOR_DATATYPE(
121  OPENPMD_ENUMERATE_TYPES)>;
122 
123 using attribute_types =
124  detail::variant_tail_t<detail::bottom OPENPMD_FOREACH_DATATYPE(
125  OPENPMD_ENUMERATE_TYPES)>;
126 
127 #undef OPENPMD_ENUMERATE_TYPES
128 
138 template <typename T, typename U>
140  : std::is_same<
141  typename std::remove_pointer<typename std::remove_cv<
142  typename std::decay<typename std::remove_all_extents<T>::type>::
143  type>::type>::type,
144  typename std::remove_pointer<typename std::remove_cv<
145  typename std::decay<typename std::remove_all_extents<U>::type>::
146  type>::type>::type>::type
147 {};
148 
149 template <typename T, typename U>
150 constexpr bool decay_equiv_v = decay_equiv<T, U>::value;
151 
152 template <typename T>
153 inline constexpr Datatype determineDatatype()
154 {
155  using DT = Datatype;
157  {
158  return DT::CHAR;
159  }
160  else if (decay_equiv<T, unsigned char>::value)
161  {
162  return DT::UCHAR;
163  }
164  else if (decay_equiv<T, signed char>::value)
165  {
166  return DT::SCHAR;
167  }
168  else if (decay_equiv<T, short>::value)
169  {
170  return DT::SHORT;
171  }
172  else if (decay_equiv<T, int>::value)
173  {
174  return DT::INT;
175  }
176  else if (decay_equiv<T, long>::value)
177  {
178  return DT::LONG;
179  }
180  else if (decay_equiv<T, long long>::value)
181  {
182  return DT::LONGLONG;
183  }
184  else if (decay_equiv<T, unsigned short>::value)
185  {
186  return DT::USHORT;
187  }
188  else if (decay_equiv<T, unsigned int>::value)
189  {
190  return DT::UINT;
191  }
192  else if (decay_equiv<T, unsigned long>::value)
193  {
194  return DT::ULONG;
195  }
196  else if (decay_equiv<T, unsigned long long>::value)
197  {
198  return DT::ULONGLONG;
199  }
200  else if (decay_equiv<T, float>::value)
201  {
202  return DT::FLOAT;
203  }
204  else if (decay_equiv<T, double>::value)
205  {
206  return DT::DOUBLE;
207  }
208  else if (decay_equiv<T, long double>::value)
209  {
210  return DT::LONG_DOUBLE;
211  }
212  else if (decay_equiv<T, std::complex<float>>::value)
213  {
214  return DT::CFLOAT;
215  }
216  else if (decay_equiv<T, std::complex<double>>::value)
217  {
218  return DT::CDOUBLE;
219  }
220  else if (decay_equiv<T, std::complex<long double>>::value)
221  {
222  return DT::CLONG_DOUBLE;
223  }
224  else if (decay_equiv<T, std::string>::value)
225  {
226  return DT::STRING;
227  }
228  else if (decay_equiv<T, std::vector<char>>::value)
229  {
230  return DT::VEC_CHAR;
231  }
232  else if (decay_equiv<T, std::vector<short>>::value)
233  {
234  return DT::VEC_SHORT;
235  }
236  else if (decay_equiv<T, std::vector<int>>::value)
237  {
238  return DT::VEC_INT;
239  }
240  else if (decay_equiv<T, std::vector<long>>::value)
241  {
242  return DT::VEC_LONG;
243  }
244  else if (decay_equiv<T, std::vector<long long>>::value)
245  {
246  return DT::VEC_LONGLONG;
247  }
248  else if (decay_equiv<T, std::vector<unsigned char>>::value)
249  {
250  return DT::VEC_UCHAR;
251  }
252  else if (decay_equiv<T, std::vector<signed char>>::value)
253  {
254  return DT::VEC_SCHAR;
255  }
256  else if (decay_equiv<T, std::vector<unsigned short>>::value)
257  {
258  return DT::VEC_USHORT;
259  }
260  else if (decay_equiv<T, std::vector<unsigned int>>::value)
261  {
262  return DT::VEC_UINT;
263  }
264  else if (decay_equiv<T, std::vector<unsigned long>>::value)
265  {
266  return DT::VEC_ULONG;
267  }
268  else if (decay_equiv<T, std::vector<unsigned long long>>::value)
269  {
270  return DT::VEC_ULONGLONG;
271  }
272  else if (decay_equiv<T, std::vector<float>>::value)
273  {
274  return DT::VEC_FLOAT;
275  }
276  else if (decay_equiv<T, std::vector<double>>::value)
277  {
278  return DT::VEC_DOUBLE;
279  }
280  else if (decay_equiv<T, std::vector<long double>>::value)
281  {
282  return DT::VEC_LONG_DOUBLE;
283  }
284  else if (decay_equiv<T, std::vector<std::complex<float>>>::value)
285  {
286  return DT::VEC_CFLOAT;
287  }
288  else if (decay_equiv<T, std::vector<std::complex<double>>>::value)
289  {
290  return DT::VEC_CDOUBLE;
291  }
292  else if (decay_equiv<T, std::vector<std::complex<long double>>>::value)
293  {
294  return DT::VEC_CLONG_DOUBLE;
295  }
296  else if (decay_equiv<T, std::vector<std::string>>::value)
297  {
298  return DT::VEC_STRING;
299  }
300  else if (decay_equiv<T, std::array<double, 7>>::value)
301  {
302  return DT::ARR_DBL_7;
303  }
304  else if (decay_equiv<T, bool>::value)
305  {
306  return DT::BOOL;
307  }
308  else
309  return Datatype::UNDEFINED;
310 }
311 
320 template <typename T>
321 inline constexpr Datatype determineDatatype(T &&val)
322 {
323  (void)val; // don't need this, it only has a name for Doxygen
324  using T_stripped = std::remove_cv_t<std::remove_reference_t<T>>;
325  if constexpr (auxiliary::IsPointer_v<T_stripped>)
326  {
327  return determineDatatype<auxiliary::IsPointer_t<T_stripped>>();
328  }
329  else if constexpr (auxiliary::IsContiguousContainer_v<T_stripped>)
330  {
331  static_assert(auxiliary::dependent_false_v<T_stripped>, R"(
332 Error: Passed a contiguous container type to determineDatatype<>().
333 These types are not directly supported due to colliding semantics.
334 Assuming a vector object `std::vector<float> vec;`,
335 use one of the following alternatives:
336 
337 1) If what you want is a direct openPMD::Datatype equivalent
338  of the container type, use:
339  `determineDatatype<decltype(vec)>()`
340  OR
341  `determineDatatype<std::vector<float>>()`.
342  The result will be `Datatype::VECTOR_FLOAT`.
343 2) If what you want is the openPMD::Datatype equivalent of the *contained type*,
344  use the raw pointer overload by:
345  `determineDatatype(vec.data())`
346  The result will be `Datatype::FLOAT`.
347  This is the variant that you likely wish to use if intending to write data
348  from the vector via `storeChunk()`, e.g. `storeChunk(vec, {0}, {10})`.
349  )");
350  }
351  else
352  {
353  static_assert(auxiliary::dependent_false_v<T_stripped>, R"(
354 Error: Unknown datatype passed to determineDatatype<>().
355 For a direct translation from C++ type to the openPMD::Datatype enum, use:
356 `auto determineDatatype<T>() -> Datatype`.
357 
358 For detecting the contained datatpye of a pointer type (shared or raw pointer),
359 use this following template (i.e. `auto determineDatatype<T>(T &&) -> Datatype`)
360 which accepts pointer-type parameters (raw, shared or unique).
361  )");
362  }
363  // Unreachable, but C++ does not know it
364  return Datatype::UNDEFINED;
365 }
366 
372 inline size_t toBytes(Datatype d)
373 {
374  using DT = Datatype;
375  switch (d)
376  {
377  case DT::CHAR:
378  case DT::VEC_CHAR:
379  case DT::STRING:
380  case DT::VEC_STRING:
381  return sizeof(char);
382  case DT::UCHAR:
383  case DT::VEC_UCHAR:
384  return sizeof(unsigned char);
385  case DT::SCHAR:
386  case DT::VEC_SCHAR:
387  return sizeof(signed char);
388  case DT::SHORT:
389  case DT::VEC_SHORT:
390  return sizeof(short);
391  case DT::INT:
392  case DT::VEC_INT:
393  return sizeof(int);
394  case DT::LONG:
395  case DT::VEC_LONG:
396  return sizeof(long);
397  case DT::LONGLONG:
398  case DT::VEC_LONGLONG:
399  return sizeof(long long);
400  case DT::USHORT:
401  case DT::VEC_USHORT:
402  return sizeof(unsigned short);
403  case DT::UINT:
404  case DT::VEC_UINT:
405  return sizeof(unsigned int);
406  case DT::ULONG:
407  case DT::VEC_ULONG:
408  return sizeof(unsigned long);
409  case DT::ULONGLONG:
410  case DT::VEC_ULONGLONG:
411  return sizeof(unsigned long long);
412  case DT::FLOAT:
413  case DT::VEC_FLOAT:
414  return sizeof(float);
415  case DT::DOUBLE:
416  case DT::VEC_DOUBLE:
417  case DT::ARR_DBL_7:
418  return sizeof(double);
419  case DT::LONG_DOUBLE:
420  case DT::VEC_LONG_DOUBLE:
421  return sizeof(long double);
422  case DT::CFLOAT:
423  case DT::VEC_CFLOAT:
424  return sizeof(float) * 2;
425  case DT::CDOUBLE:
426  case DT::VEC_CDOUBLE:
427  return sizeof(double) * 2;
428  case DT::CLONG_DOUBLE:
429  case DT::VEC_CLONG_DOUBLE:
430  return sizeof(long double) * 2;
431  case DT::BOOL:
432  return sizeof(bool);
433  case DT::UNDEFINED:
434  default:
435  throw std::runtime_error("toBytes: Invalid datatype!");
436  }
437 }
438 
444 inline size_t toBits(Datatype d)
445 {
446  return toBytes(d) * CHAR_BIT;
447 }
448 
454 inline bool isVector(Datatype d)
455 {
456  using DT = Datatype;
457 
458  switch (d)
459  {
460  case DT::VEC_CHAR:
461  case DT::VEC_SHORT:
462  case DT::VEC_INT:
463  case DT::VEC_LONG:
464  case DT::VEC_LONGLONG:
465  case DT::VEC_UCHAR:
466  case DT::VEC_USHORT:
467  case DT::VEC_UINT:
468  case DT::VEC_ULONG:
469  case DT::VEC_ULONGLONG:
470  case DT::VEC_FLOAT:
471  case DT::VEC_DOUBLE:
472  case DT::VEC_LONG_DOUBLE:
473  case DT::VEC_CFLOAT:
474  case DT::VEC_CDOUBLE:
475  case DT::VEC_CLONG_DOUBLE:
476  case DT::VEC_STRING:
477  return true;
478  default:
479  return false;
480  }
481 }
482 
490 inline bool isFloatingPoint(Datatype d)
491 {
492  using DT = Datatype;
493 
494  switch (d)
495  {
496  case DT::FLOAT:
497  case DT::VEC_FLOAT:
498  case DT::DOUBLE:
499  case DT::VEC_DOUBLE:
500  case DT::LONG_DOUBLE:
501  case DT::VEC_LONG_DOUBLE:
502  // note: complex floats are not std::is_floating_point
503  return true;
504  default:
505  return false;
506  }
507 }
508 
517 {
518  using DT = Datatype;
519 
520  switch (d)
521  {
522  case DT::CFLOAT:
523  case DT::VEC_CFLOAT:
524  case DT::CDOUBLE:
525  case DT::VEC_CDOUBLE:
526  case DT::CLONG_DOUBLE:
527  case DT::VEC_CLONG_DOUBLE:
528  return true;
529  default:
530  return false;
531  }
532 }
533 
541 template <typename T>
542 inline bool isFloatingPoint()
543 {
544  Datatype dtype = determineDatatype<T>();
545 
546  return isFloatingPoint(dtype);
547 }
548 
556 template <typename T>
558 {
559  Datatype dtype = determineDatatype<T>();
560 
561  return isComplexFloatingPoint(dtype);
562 }
563 
572 inline std::tuple<bool, bool> isInteger(Datatype d)
573 {
574  using DT = Datatype;
575 
576  switch (d)
577  {
578  case DT::SHORT:
579  case DT::VEC_SHORT:
580  case DT::INT:
581  case DT::VEC_INT:
582  case DT::LONG:
583  case DT::VEC_LONG:
584  case DT::LONGLONG:
585  case DT::VEC_LONGLONG:
586  return std::make_tuple(true, true);
587  case DT::USHORT:
588  case DT::VEC_USHORT:
589  case DT::UINT:
590  case DT::VEC_UINT:
591  case DT::ULONG:
592  case DT::VEC_ULONG:
593  case DT::ULONGLONG:
594  case DT::VEC_ULONGLONG:
595  return std::make_tuple(true, false);
596  default:
597  return std::make_tuple(false, false);
598  }
599 }
600 
609 template <typename T>
610 inline std::tuple<bool, bool> isInteger()
611 {
612  Datatype dtype = determineDatatype<T>();
613 
614  return isInteger(dtype);
615 }
616 
623 template <typename T_FP>
625 {
626  // template
627  bool tt_is_fp = isFloatingPoint<T_FP>();
628 
629  // Datatype
630  bool dt_is_fp = isFloatingPoint(d);
631 
632  if (tt_is_fp && dt_is_fp && toBits(d) == toBits(determineDatatype<T_FP>()))
633  return true;
634  else
635  return false;
636 }
637 
645 template <typename T_CFP>
647 {
648  // template
649  bool tt_is_cfp = isComplexFloatingPoint<T_CFP>();
650 
651  // Datatype
652  bool dt_is_cfp = isComplexFloatingPoint(d);
653 
654  if (tt_is_cfp && dt_is_cfp &&
655  toBits(d) == toBits(determineDatatype<T_CFP>()))
656  return true;
657  else
658  return false;
659 }
660 
668 template <typename T_Int>
669 inline bool isSameInteger(Datatype d)
670 {
671  // template
672  bool tt_is_int, tt_is_sig;
673  std::tie(tt_is_int, tt_is_sig) = isInteger<T_Int>();
674 
675  // Datatype
676  bool dt_is_int, dt_is_sig;
677  std::tie(dt_is_int, dt_is_sig) = isInteger(d);
678 
679  if (tt_is_int && dt_is_int && tt_is_sig == dt_is_sig &&
680  toBits(d) == toBits(determineDatatype<T_Int>()))
681  return true;
682  else
683  return false;
684 }
685 
693 constexpr bool isChar(Datatype d)
694 {
695  switch (d)
696  {
697  case Datatype::CHAR:
698  case Datatype::SCHAR:
699  case Datatype::UCHAR:
700  return true;
701  default:
702  return false;
703  }
704 }
705 
718 template <typename T_Char>
719 constexpr bool isSameChar(Datatype d);
720 
727 inline bool isSame(openPMD::Datatype const d, openPMD::Datatype const e)
728 {
729  // exact same type
730  if (static_cast<int>(d) == static_cast<int>(e))
731  return true;
732 
733  bool d_is_vec = isVector(d);
734  bool e_is_vec = isVector(e);
735 
736  // same int
737  bool d_is_int, d_is_sig;
738  std::tie(d_is_int, d_is_sig) = isInteger(d);
739  bool e_is_int, e_is_sig;
740  std::tie(e_is_int, e_is_sig) = isInteger(e);
741  if (d_is_int && e_is_int && d_is_vec == e_is_vec && d_is_sig == e_is_sig &&
742  toBits(d) == toBits(e))
743  return true;
744 
745  // same float
746  bool d_is_fp = isFloatingPoint(d);
747  bool e_is_fp = isFloatingPoint(e);
748 
749  if (d_is_fp && e_is_fp && d_is_vec == e_is_vec && toBits(d) == toBits(e))
750  return true;
751 
752  // same complex floating point
753  bool d_is_cfp = isComplexFloatingPoint(d);
754  bool e_is_cfp = isComplexFloatingPoint(e);
755 
756  if (d_is_cfp && e_is_cfp && d_is_vec == e_is_vec && toBits(d) == toBits(e))
757  return true;
758 
759  return false;
760 }
761 
769 
770 Datatype toVectorType(Datatype dt);
771 
772 std::string datatypeToString(Datatype dt);
773 
774 Datatype stringToDatatype(const std::string &s);
775 
776 void warnWrongDtype(std::string const &key, Datatype store, Datatype request);
777 
778 std::ostream &operator<<(std::ostream &, openPMD::Datatype const &);
779 
794 template <typename Action, typename... Args>
795 constexpr auto switchType(Datatype dt, Args &&...args)
796  -> decltype(Action::template call<char>(std::forward<Args>(args)...));
797 
813 template <typename Action, typename... Args>
814 constexpr auto switchNonVectorType(Datatype dt, Args &&...args)
815  -> decltype(Action::template call<char>(std::forward<Args>(args)...));
816 
832 template <typename Action, typename... Args>
833 constexpr auto switchDatasetType(Datatype dt, Args &&...args)
834  -> decltype(Action::template call<char>(std::forward<Args>(args)...));
835 
836 } // namespace openPMD
837 
838 #if !defined(_MSC_VER)
851 inline bool operator==(openPMD::Datatype d, openPMD::Datatype e)
852 {
853  return openPMD::isSame(d, e);
854 }
855 
856 inline bool operator!=(openPMD::Datatype d, openPMD::Datatype e)
857 {
858  return !(d == e);
859 }
862 #endif
863 
864 #include "openPMD/Datatype.tpp"
865 #include "openPMD/UndefDatatypeMacros.hpp"
Public definitions of openPMD-api.
Definition: Date.cpp:29
bool isVector(Datatype d)
Compare if a Datatype is a vector type.
Definition: Datatype.hpp:454
constexpr bool isSameChar(Datatype d)
Determines if d and T_Char are char types of same representation.
bool isSameFloatingPoint(Datatype d)
Compare if a Datatype is equivalent to a floating point type.
Definition: Datatype.hpp:624
bool isSameComplexFloatingPoint(Datatype d)
Compare if a Datatype is equivalent to a complex floating point type.
Definition: Datatype.hpp:646
size_t toBits(Datatype d)
Return number of bits representing a Datatype.
Definition: Datatype.hpp:444
bool isComplexFloatingPoint(Datatype d)
Compare if a Datatype is a complex floating point type.
Definition: Datatype.hpp:516
constexpr bool isChar(Datatype d)
Determines if d represents a char type.
Definition: Datatype.hpp:693
std::vector< Datatype > openPMD_Datatypes()
All openPMD datatypes defined in Datatype, listed in order in a vector.
Definition: Datatype.cpp:227
bool isFloatingPoint(Datatype d)
Compare if a Datatype is a floating point type.
Definition: Datatype.hpp:490
std::tuple< bool, bool > isInteger(Datatype d)
Compare if a Datatype is an integer type.
Definition: Datatype.hpp:572
bool isSame(openPMD::Datatype const d, openPMD::Datatype const e)
Comparison for two Datatypes.
Definition: Datatype.hpp:727
size_t toBytes(Datatype d)
Return number of bytes representing a Datatype.
Definition: Datatype.hpp:372
bool isSameInteger(Datatype d)
Compare if a Datatype is equivalent to an integer type.
Definition: Datatype.hpp:669
Datatype
Concrete datatype of an object available at runtime.
Definition: Datatype.hpp:51
constexpr auto switchType(Datatype dt, Args &&...args) -> decltype(Action::template call< char >(std::forward< Args >(args)...))
Generalizes switching over an openPMD datatype.
constexpr auto switchNonVectorType(Datatype dt, Args &&...args) -> decltype(Action::template call< char >(std::forward< Args >(args)...))
Generalizes switching over an openPMD datatype.
Datatype basicDatatype(Datatype dt)
basicDatatype Strip openPMD Datatype of std::vector, std::array et.
Definition: Datatype.cpp:289
constexpr auto switchDatasetType(Datatype dt, Args &&...args) -> decltype(Action::template call< char >(std::forward< Args >(args)...))
Generalizes switching over an openPMD datatype.
Fundamental equivalence check for two given types T and U.
Definition: Datatype.hpp:147
Definition: Datatype.hpp:105