Skip to content

Commit 603305b

Browse files
committed
Unify code paths for both get() and getOptional()
Note that this is slightly API breaking since the shared code was in a public header. All implementation details are now in the detail namespace.
1 parent 2bb99dd commit 603305b

File tree

1 file changed

+109
-198
lines changed

1 file changed

+109
-198
lines changed

include/openPMD/backend/Attribute.hpp

Lines changed: 109 additions & 198 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include <string>
3535
#include <type_traits>
3636
#include <utility>
37+
#include <variant>
3738
#include <vector>
3839

3940
namespace openPMD
@@ -134,202 +135,101 @@ class Attribute
134135
std::optional<U> getOptional() const;
135136
};
136137

137-
template <typename T, typename U>
138-
auto doConvert(T *pv) -> U
138+
namespace detail
139139
{
140-
(void)pv;
141-
if constexpr (std::is_convertible_v<T, U>)
140+
template <typename T, typename U>
141+
auto doConvert(T *pv) -> std::variant<U, std::runtime_error>
142142
{
143-
return static_cast<U>(*pv);
144-
}
145-
else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsVector_v<U>)
146-
{
147-
if constexpr (std::is_convertible_v<
148-
typename T::value_type,
149-
typename U::value_type>)
143+
(void)pv;
144+
if constexpr (std::is_convertible_v<T, U>)
150145
{
151-
U res{};
152-
res.reserve(pv->size());
153-
std::copy(pv->begin(), pv->end(), std::back_inserter(res));
154-
return res;
146+
return static_cast<U>(*pv);
155147
}
156-
else
157-
{
158-
throw std::runtime_error("getCast: no vector cast possible.");
159-
}
160-
}
161-
// conversion cast: array to vector
162-
// if a backend reports a std::array<> for something where
163-
// the frontend expects a vector
164-
else if constexpr (auxiliary::IsArray_v<T> && auxiliary::IsVector_v<U>)
165-
{
166-
if constexpr (std::is_convertible_v<
167-
typename T::value_type,
168-
typename U::value_type>)
169-
{
170-
U res{};
171-
res.reserve(pv->size());
172-
std::copy(pv->begin(), pv->end(), std::back_inserter(res));
173-
return res;
174-
}
175-
else
176-
{
177-
throw std::runtime_error(
178-
"getCast: no array to vector conversion possible.");
179-
}
180-
}
181-
// conversion cast: vector to array
182-
// if a backend reports a std::vector<> for something where
183-
// the frontend expects an array
184-
else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsArray_v<U>)
185-
{
186-
if constexpr (std::is_convertible_v<
187-
typename T::value_type,
188-
typename U::value_type>)
148+
else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsVector_v<U>)
189149
{
190-
U res{};
191-
if (res.size() != pv->size())
150+
if constexpr (std::is_convertible_v<
151+
typename T::value_type,
152+
typename U::value_type>)
192153
{
193-
throw std::runtime_error(
194-
"getCast: no vector to array conversion possible (wrong "
195-
"requested array size).");
154+
U res{};
155+
res.reserve(pv->size());
156+
std::copy(pv->begin(), pv->end(), std::back_inserter(res));
157+
return res;
196158
}
197-
for (size_t i = 0; i < res.size(); ++i)
159+
else
198160
{
199-
res[i] = static_cast<typename U::value_type>((*pv)[i]);
161+
return std::runtime_error("getCast: no vector cast possible.");
200162
}
201-
return res;
202163
}
203-
else
164+
// conversion cast: array to vector
165+
// if a backend reports a std::array<> for something where
166+
// the frontend expects a vector
167+
else if constexpr (auxiliary::IsArray_v<T> && auxiliary::IsVector_v<U>)
204168
{
205-
throw std::runtime_error(
206-
"getCast: no vector to array conversion possible.");
207-
}
208-
}
209-
// conversion cast: turn a single value into a 1-element vector
210-
else if constexpr (auxiliary::IsVector_v<U>)
211-
{
212-
if constexpr (std::is_convertible_v<T, typename U::value_type>)
213-
{
214-
U res{};
215-
res.reserve(1);
216-
res.push_back(static_cast<typename U::value_type>(*pv));
217-
return res;
218-
}
219-
else
220-
{
221-
throw std::runtime_error(
222-
"getCast: no scalar to vector conversion possible.");
223-
}
224-
}
225-
else
226-
{
227-
throw std::runtime_error("getCast: no cast possible.");
228-
}
229-
#if defined(__INTEL_COMPILER)
230-
/*
231-
* ICPC has trouble with if constexpr, thinking that return statements are
232-
* missing afterwards. Deactivate the warning.
233-
* Note that putting a statement here will not help to fix this since it will
234-
* then complain about unreachable code.
235-
* https://community.intel.com/t5/Intel-C-Compiler/quot-if-constexpr-quot-and-quot-missing-return-statement-quot-in/td-p/1154551
236-
*/
237-
#pragma warning(disable : 1011)
238-
}
239-
#pragma warning(default : 1011)
240-
#else
241-
}
242-
#endif
243-
244-
template <typename T, typename U>
245-
auto doConvertOptional(T *pv) -> std::optional<U>
246-
{
247-
(void)pv;
248-
if constexpr (std::is_convertible_v<T, U>)
249-
{
250-
return static_cast<U>(*pv);
251-
}
252-
else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsVector_v<U>)
253-
{
254-
if constexpr (std::is_convertible_v<
255-
typename T::value_type,
256-
typename U::value_type>)
257-
{
258-
U res{};
259-
res.reserve(pv->size());
260-
std::copy(pv->begin(), pv->end(), std::back_inserter(res));
261-
return res;
262-
}
263-
else
264-
{
265-
return {};
266-
}
267-
}
268-
// conversion cast: array to vector
269-
// if a backend reports a std::array<> for something where
270-
// the frontend expects a vector
271-
else if constexpr (auxiliary::IsArray_v<T> && auxiliary::IsVector_v<U>)
272-
{
273-
if constexpr (std::is_convertible_v<
274-
typename T::value_type,
275-
typename U::value_type>)
276-
{
277-
U res{};
278-
res.reserve(pv->size());
279-
std::copy(pv->begin(), pv->end(), std::back_inserter(res));
280-
return res;
281-
}
282-
else
283-
{
284-
return {};
285-
}
286-
}
287-
// conversion cast: vector to array
288-
// if a backend reports a std::vector<> for something where
289-
// the frontend expects an array
290-
else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsArray_v<U>)
291-
{
292-
if constexpr (std::is_convertible_v<
293-
typename T::value_type,
294-
typename U::value_type>)
295-
{
296-
U res{};
297-
if (res.size() != pv->size())
169+
if constexpr (std::is_convertible_v<
170+
typename T::value_type,
171+
typename U::value_type>)
298172
{
299-
throw std::runtime_error(
300-
"getCast: no vector to array conversion possible (wrong "
301-
"requested array size).");
173+
U res{};
174+
res.reserve(pv->size());
175+
std::copy(pv->begin(), pv->end(), std::back_inserter(res));
176+
return res;
302177
}
303-
for (size_t i = 0; i < res.size(); ++i)
178+
else
304179
{
305-
res[i] = static_cast<typename U::value_type>((*pv)[i]);
180+
return std::runtime_error(
181+
"getCast: no array to vector conversion possible.");
306182
}
307-
return res;
308183
}
309-
else
184+
// conversion cast: vector to array
185+
// if a backend reports a std::vector<> for something where
186+
// the frontend expects an array
187+
else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsArray_v<U>)
310188
{
311-
return {};
189+
if constexpr (std::is_convertible_v<
190+
typename T::value_type,
191+
typename U::value_type>)
192+
{
193+
U res{};
194+
if (res.size() != pv->size())
195+
{
196+
return std::runtime_error(
197+
"getCast: no vector to array conversion possible "
198+
"(wrong "
199+
"requested array size).");
200+
}
201+
for (size_t i = 0; i < res.size(); ++i)
202+
{
203+
res[i] = static_cast<typename U::value_type>((*pv)[i]);
204+
}
205+
return res;
206+
}
207+
else
208+
{
209+
return std::runtime_error(
210+
"getCast: no vector to array conversion possible.");
211+
}
312212
}
313-
}
314-
// conversion cast: turn a single value into a 1-element vector
315-
else if constexpr (auxiliary::IsVector_v<U>)
316-
{
317-
if constexpr (std::is_convertible_v<T, typename U::value_type>)
213+
// conversion cast: turn a single value into a 1-element vector
214+
else if constexpr (auxiliary::IsVector_v<U>)
318215
{
319-
U res{};
320-
res.reserve(1);
321-
res.push_back(static_cast<typename U::value_type>(*pv));
322-
return res;
216+
if constexpr (std::is_convertible_v<T, typename U::value_type>)
217+
{
218+
U res{};
219+
res.reserve(1);
220+
res.push_back(static_cast<typename U::value_type>(*pv));
221+
return res;
222+
}
223+
else
224+
{
225+
return std::runtime_error(
226+
"getCast: no scalar to vector conversion possible.");
227+
}
323228
}
324229
else
325230
{
326-
return {};
231+
return std::runtime_error("getCast: no cast possible.");
327232
}
328-
}
329-
else
330-
{
331-
return {};
332-
}
333233
#if defined(__INTEL_COMPILER)
334234
/*
335235
* ICPC has trouble with if constexpr, thinking that return statements are
@@ -342,44 +242,55 @@ auto doConvertOptional(T *pv) -> std::optional<U>
342242
}
343243
#pragma warning(default : 1011)
344244
#else
345-
}
245+
}
346246
#endif
247+
} // namespace detail
347248

348-
/** Retrieve a stored specific Attribute and cast if convertible.
349-
*
350-
* @throw std::runtime_error if stored object is not static castable to U.
351-
* @tparam U Type of the object to be casted to.
352-
* @return Copy of the retrieved object, casted to type U.
353-
*/
354249
template <typename U>
355-
inline U getCast(Attribute const &a)
250+
U Attribute::get() const
356251
{
357-
auto v = a.getResource();
358-
252+
auto eitherValueOrError = std::visit(
253+
[](auto &&containedValue) -> std::variant<U, std::runtime_error> {
254+
using containedType = std::decay_t<decltype(containedValue)>;
255+
return detail::doConvert<containedType, U>(&containedValue);
256+
},
257+
Variant::getResource());
359258
return std::visit(
360259
[](auto &&containedValue) -> U {
361-
using containedType = std::decay_t<decltype(containedValue)>;
362-
return doConvert<containedType, U>(&containedValue);
260+
using T = std::decay_t<decltype(containedValue)>;
261+
if constexpr (std::is_same_v<T, std::runtime_error>)
262+
{
263+
throw containedValue;
264+
}
265+
else
266+
{
267+
return std::forward<T>(containedValue);
268+
}
363269
},
364-
v);
365-
}
366-
367-
template <typename U>
368-
U Attribute::get() const
369-
{
370-
return getCast<U>(Variant::getResource());
270+
std::move(eitherValueOrError));
371271
}
372272

373273
template <typename U>
374274
std::optional<U> Attribute::getOptional() const
375275
{
376-
auto v = Variant::getResource();
377-
378-
return std::visit(
379-
[](auto &&containedValue) -> U {
276+
auto eitherValueOrError = std::visit(
277+
[](auto &&containedValue) -> std::variant<U, std::runtime_error> {
380278
using containedType = std::decay_t<decltype(containedValue)>;
381-
return doConvert<containedType, U>(&containedValue);
279+
return detail::doConvert<containedType, U>(&containedValue);
280+
},
281+
Variant::getResource());
282+
return std::visit(
283+
[](auto &&containedValue) -> std::optional<U> {
284+
using T = std::decay_t<decltype(containedValue)>;
285+
if constexpr (std::is_same_v<T, std::runtime_error>)
286+
{
287+
return std::nullopt;
288+
}
289+
else
290+
{
291+
return {std::forward<T>(containedValue)};
292+
}
382293
},
383-
v);
294+
std::move(eitherValueOrError));
384295
}
385296
} // namespace openPMD

0 commit comments

Comments
 (0)