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