29
29
#include < complex>
30
30
#include < cstdint>
31
31
#include < iterator>
32
+ #include < optional>
32
33
#include < stdexcept>
33
34
#include < string>
34
35
#include < type_traits>
35
36
#include < utility>
37
+ #include < variant>
36
38
#include < vector>
37
39
38
40
namespace openPMD
@@ -119,100 +121,117 @@ class Attribute
119
121
*/
120
122
template <typename U>
121
123
U get () const ;
124
+
125
+ /* * Retrieve a stored specific Attribute and cast if convertible.
126
+ * Like Attribute::get<>(), but returns an empty std::optional if no
127
+ * conversion is possible instead of throwing an exception.
128
+ *
129
+ * @note This performs a static_cast and might introduce precision loss if
130
+ * requested. Check dtype explicitly beforehand if needed.
131
+ *
132
+ * @tparam U Type of the object to be casted to.
133
+ * @return Copy of the retrieved object, casted to type U.
134
+ * An empty std::optional if no conversion is possible.
135
+ */
136
+ template <typename U>
137
+ std::optional<U> getOptional () const ;
122
138
};
123
139
124
- template <typename T, typename U>
125
- auto doConvert (T *pv) -> U
140
+ namespace detail
126
141
{
127
- (void )pv;
128
- if constexpr (std::is_convertible_v<T, U>)
129
- {
130
- return static_cast <U>(*pv);
131
- }
132
- else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsVector_v<U>)
142
+ template <typename T, typename U>
143
+ auto doConvert (T *pv) -> std::variant<U, std::runtime_error>
133
144
{
134
- if constexpr (std::is_convertible_v<
135
- typename T::value_type,
136
- typename U::value_type>)
137
- {
138
- U res{};
139
- res.reserve (pv->size ());
140
- std::copy (pv->begin (), pv->end (), std::back_inserter (res));
141
- return res;
142
- }
143
- else
145
+ (void )pv;
146
+ if constexpr (std::is_convertible_v<T, U>)
144
147
{
145
- throw std::runtime_error ( " getCast: no vector cast possible. " );
148
+ return static_cast <U>(*pv );
146
149
}
147
- }
148
- // conversion cast: array to vector
149
- // if a backend reports a std::array<> for something where
150
- // the frontend expects a vector
151
- else if constexpr (auxiliary::IsArray_v<T> && auxiliary::IsVector_v<U>)
152
- {
153
- if constexpr (std::is_convertible_v<
154
- typename T::value_type,
155
- typename U::value_type>)
150
+ else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsVector_v<U>)
156
151
{
157
- U res{};
158
- res.reserve (pv->size ());
159
- std::copy (pv->begin (), pv->end (), std::back_inserter (res));
160
- return res;
161
- }
162
- else
163
- {
164
- throw std::runtime_error (
165
- " getCast: no array to vector conversion possible." );
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 std::runtime_error (" getCast: no vector cast possible." );
164
+ }
166
165
}
167
- }
168
- // conversion cast: vector to array
169
- // if a backend reports a std::vector<> for something where
170
- // the frontend expects an array
171
- else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsArray_v<U>)
172
- {
173
- if constexpr (std::is_convertible_v<
174
- typename T::value_type,
175
- typename U::value_type>)
166
+ // conversion cast: array to vector
167
+ // if a backend reports a std::array<> for something where
168
+ // the frontend expects a vector
169
+ else if constexpr (auxiliary::IsArray_v<T> && auxiliary::IsVector_v<U>)
176
170
{
177
- U res{};
178
- if (res.size () != pv->size ())
171
+ if constexpr (std::is_convertible_v<
172
+ typename T::value_type,
173
+ typename U::value_type>)
179
174
{
180
- throw std::runtime_error (
181
- " getCast: no vector to array conversion possible (wrong "
182
- " requested array size)." );
175
+ U res{};
176
+ res.reserve (pv->size ());
177
+ std::copy (pv->begin (), pv->end (), std::back_inserter (res));
178
+ return res;
183
179
}
184
- for ( size_t i = 0 ; i < res. size (); ++i)
180
+ else
185
181
{
186
- res[i] = static_cast <typename U::value_type>((*pv)[i]);
182
+ return std::runtime_error (
183
+ " getCast: no array to vector conversion possible." );
187
184
}
188
- return res;
189
185
}
190
- else
186
+ // conversion cast: vector to array
187
+ // if a backend reports a std::vector<> for something where
188
+ // the frontend expects an array
189
+ else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsArray_v<U>)
191
190
{
192
- throw std::runtime_error (
193
- " getCast: no vector to array conversion possible." );
191
+ if constexpr (std::is_convertible_v<
192
+ typename T::value_type,
193
+ typename U::value_type>)
194
+ {
195
+ U res{};
196
+ if (res.size () != pv->size ())
197
+ {
198
+ return std::runtime_error (
199
+ " getCast: no vector to array conversion possible "
200
+ " (wrong "
201
+ " requested array size)." );
202
+ }
203
+ for (size_t i = 0 ; i < res.size (); ++i)
204
+ {
205
+ res[i] = static_cast <typename U::value_type>((*pv)[i]);
206
+ }
207
+ return res;
208
+ }
209
+ else
210
+ {
211
+ return std::runtime_error (
212
+ " getCast: no vector to array conversion possible." );
213
+ }
194
214
}
195
- }
196
- // conversion cast: turn a single value into a 1-element vector
197
- else if constexpr (auxiliary::IsVector_v<U>)
198
- {
199
- if constexpr (std::is_convertible_v<T, typename U::value_type>)
215
+ // conversion cast: turn a single value into a 1-element vector
216
+ else if constexpr (auxiliary::IsVector_v<U>)
200
217
{
201
- U res{};
202
- res.reserve (1 );
203
- res.push_back (static_cast <typename U::value_type>(*pv));
204
- return res;
218
+ if constexpr (std::is_convertible_v<T, typename U::value_type>)
219
+ {
220
+ U res{};
221
+ res.reserve (1 );
222
+ res.push_back (static_cast <typename U::value_type>(*pv));
223
+ return res;
224
+ }
225
+ else
226
+ {
227
+ return std::runtime_error (
228
+ " getCast: no scalar to vector conversion possible." );
229
+ }
205
230
}
206
231
else
207
232
{
208
- throw std::runtime_error (
209
- " getCast: no scalar to vector conversion possible." );
233
+ return std::runtime_error (" getCast: no cast possible." );
210
234
}
211
- }
212
- else
213
- {
214
- throw std::runtime_error (" getCast: no cast possible." );
215
- }
216
235
#if defined(__INTEL_COMPILER)
217
236
/*
218
237
* ICPC has trouble with if constexpr, thinking that return statements are
@@ -222,35 +241,58 @@ auto doConvert(T *pv) -> U
222
241
* https://community.intel.com/t5/Intel-C-Compiler/quot-if-constexpr-quot-and-quot-missing-return-statement-quot-in/td-p/1154551
223
242
*/
224
243
#pragma warning(disable : 1011)
225
- }
244
+ }
226
245
#pragma warning(default : 1011)
227
246
#else
228
- }
247
+ }
229
248
#endif
249
+ } // namespace detail
230
250
231
- /* * Retrieve a stored specific Attribute and cast if convertible.
232
- *
233
- * @throw std::runtime_error if stored object is not static castable to U.
234
- * @tparam U Type of the object to be casted to.
235
- * @return Copy of the retrieved object, casted to type U.
236
- */
237
251
template <typename U>
238
- inline U getCast ( Attribute const &a)
252
+ U Attribute::get () const
239
253
{
240
- auto v = a.getResource ();
241
-
254
+ auto eitherValueOrError = std::visit (
255
+ [](auto &&containedValue) -> std::variant<U, std::runtime_error> {
256
+ using containedType = std::decay_t <decltype (containedValue)>;
257
+ return detail::doConvert<containedType, U>(&containedValue);
258
+ },
259
+ Variant::getResource ());
242
260
return std::visit (
243
261
[](auto &&containedValue) -> U {
244
- using containedType = std::decay_t <decltype (containedValue)>;
245
- return doConvert<containedType, U>(&containedValue);
262
+ using T = std::decay_t <decltype (containedValue)>;
263
+ if constexpr (std::is_same_v<T, std::runtime_error>)
264
+ {
265
+ throw std::move (containedValue);
266
+ }
267
+ else
268
+ {
269
+ return std::move (containedValue);
270
+ }
246
271
},
247
- v );
272
+ std::move (eitherValueOrError) );
248
273
}
249
274
250
275
template <typename U>
251
- U Attribute::get () const
276
+ std::optional<U> Attribute::getOptional () const
252
277
{
253
- return getCast<U>(Variant::getResource ());
278
+ auto eitherValueOrError = std::visit (
279
+ [](auto &&containedValue) -> std::variant<U, std::runtime_error> {
280
+ using containedType = std::decay_t <decltype (containedValue)>;
281
+ return detail::doConvert<containedType, U>(&containedValue);
282
+ },
283
+ Variant::getResource ());
284
+ return std::visit (
285
+ [](auto &&containedValue) -> std::optional<U> {
286
+ using T = std::decay_t <decltype (containedValue)>;
287
+ if constexpr (std::is_same_v<T, std::runtime_error>)
288
+ {
289
+ return std::nullopt;
290
+ }
291
+ else
292
+ {
293
+ return {std::move (containedValue)};
294
+ }
295
+ },
296
+ std::move (eitherValueOrError));
254
297
}
255
-
256
298
} // namespace openPMD
0 commit comments