Skip to content

Commit 73446d3

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 69c9969 commit 73446d3

File tree

1 file changed

+110
-199
lines changed

1 file changed

+110
-199
lines changed

include/openPMD/backend/Attribute.hpp

Lines changed: 110 additions & 199 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
@@ -136,202 +137,101 @@ class Attribute
136137
std::optional<U> getOptional() const;
137138
};
138139

139-
template <typename T, typename U>
140-
auto doConvert(T *pv) -> U
140+
namespace detail
141141
{
142-
(void)pv;
143-
if constexpr (std::is_convertible_v<T, U>)
142+
template <typename T, typename U>
143+
auto doConvert(T *pv) -> std::variant<U, std::runtime_error>
144144
{
145-
return static_cast<U>(*pv);
146-
}
147-
else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsVector_v<U>)
148-
{
149-
if constexpr (std::is_convertible_v<
150-
typename T::value_type,
151-
typename U::value_type>)
145+
(void)pv;
146+
if constexpr (std::is_convertible_v<T, U>)
152147
{
153-
U res{};
154-
res.reserve(pv->size());
155-
std::copy(pv->begin(), pv->end(), std::back_inserter(res));
156-
return res;
148+
return static_cast<U>(*pv);
157149
}
158-
else
159-
{
160-
throw std::runtime_error("getCast: no vector cast possible.");
161-
}
162-
}
163-
// conversion cast: array to vector
164-
// if a backend reports a std::array<> for something where
165-
// the frontend expects a vector
166-
else if constexpr (auxiliary::IsArray_v<T> && auxiliary::IsVector_v<U>)
167-
{
168-
if constexpr (std::is_convertible_v<
169-
typename T::value_type,
170-
typename U::value_type>)
171-
{
172-
U res{};
173-
res.reserve(pv->size());
174-
std::copy(pv->begin(), pv->end(), std::back_inserter(res));
175-
return res;
176-
}
177-
else
178-
{
179-
throw std::runtime_error(
180-
"getCast: no array to vector conversion possible.");
181-
}
182-
}
183-
// conversion cast: vector to array
184-
// if a backend reports a std::vector<> for something where
185-
// the frontend expects an array
186-
else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsArray_v<U>)
187-
{
188-
if constexpr (std::is_convertible_v<
189-
typename T::value_type,
190-
typename U::value_type>)
150+
else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsVector_v<U>)
191151
{
192-
U res{};
193-
if (res.size() != pv->size())
152+
if constexpr (std::is_convertible_v<
153+
typename T::value_type,
154+
typename U::value_type>)
194155
{
195-
throw std::runtime_error(
196-
"getCast: no vector to array conversion possible (wrong "
197-
"requested array size).");
156+
U res{};
157+
res.reserve(pv->size());
158+
std::copy(pv->begin(), pv->end(), std::back_inserter(res));
159+
return res;
198160
}
199-
for (size_t i = 0; i < res.size(); ++i)
161+
else
200162
{
201-
res[i] = static_cast<typename U::value_type>((*pv)[i]);
163+
return std::runtime_error("getCast: no vector cast possible.");
202164
}
203-
return res;
204-
}
205-
else
206-
{
207-
throw std::runtime_error(
208-
"getCast: no vector to array conversion possible.");
209-
}
210-
}
211-
// conversion cast: turn a single value into a 1-element vector
212-
else if constexpr (auxiliary::IsVector_v<U>)
213-
{
214-
if constexpr (std::is_convertible_v<T, typename U::value_type>)
215-
{
216-
U res{};
217-
res.reserve(1);
218-
res.push_back(static_cast<typename U::value_type>(*pv));
219-
return res;
220-
}
221-
else
222-
{
223-
throw std::runtime_error(
224-
"getCast: no scalar to vector conversion possible.");
225-
}
226-
}
227-
else
228-
{
229-
throw std::runtime_error("getCast: no cast possible.");
230-
}
231-
#if defined(__INTEL_COMPILER)
232-
/*
233-
* ICPC has trouble with if constexpr, thinking that return statements are
234-
* missing afterwards. Deactivate the warning.
235-
* Note that putting a statement here will not help to fix this since it will
236-
* then complain about unreachable code.
237-
* https://community.intel.com/t5/Intel-C-Compiler/quot-if-constexpr-quot-and-quot-missing-return-statement-quot-in/td-p/1154551
238-
*/
239-
#pragma warning(disable : 1011)
240-
}
241-
#pragma warning(default : 1011)
242-
#else
243-
}
244-
#endif
245-
246-
template <typename T, typename U>
247-
auto doConvertOptional(T *pv) -> std::optional<U>
248-
{
249-
(void)pv;
250-
if constexpr (std::is_convertible_v<T, U>)
251-
{
252-
return static_cast<U>(*pv);
253-
}
254-
else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsVector_v<U>)
255-
{
256-
if constexpr (std::is_convertible_v<
257-
typename T::value_type,
258-
typename U::value_type>)
259-
{
260-
U res{};
261-
res.reserve(pv->size());
262-
std::copy(pv->begin(), pv->end(), std::back_inserter(res));
263-
return res;
264165
}
265-
else
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>)
266170
{
267-
return {};
268-
}
269-
}
270-
// conversion cast: array to vector
271-
// if a backend reports a std::array<> for something where
272-
// the frontend expects a vector
273-
else if constexpr (auxiliary::IsArray_v<T> && auxiliary::IsVector_v<U>)
274-
{
275-
if constexpr (std::is_convertible_v<
276-
typename T::value_type,
277-
typename U::value_type>)
278-
{
279-
U res{};
280-
res.reserve(pv->size());
281-
std::copy(pv->begin(), pv->end(), std::back_inserter(res));
282-
return res;
283-
}
284-
else
285-
{
286-
return {};
287-
}
288-
}
289-
// conversion cast: vector to array
290-
// if a backend reports a std::vector<> for something where
291-
// the frontend expects an array
292-
else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsArray_v<U>)
293-
{
294-
if constexpr (std::is_convertible_v<
295-
typename T::value_type,
296-
typename U::value_type>)
297-
{
298-
U res{};
299-
if (res.size() != pv->size())
171+
if constexpr (std::is_convertible_v<
172+
typename T::value_type,
173+
typename U::value_type>)
300174
{
301-
throw std::runtime_error(
302-
"getCast: no vector to array conversion possible (wrong "
303-
"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;
304179
}
305-
for (size_t i = 0; i < res.size(); ++i)
180+
else
306181
{
307-
res[i] = static_cast<typename U::value_type>((*pv)[i]);
182+
return std::runtime_error(
183+
"getCast: no array to vector conversion possible.");
308184
}
309-
return res;
310185
}
311-
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>)
312190
{
313-
return {};
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+
}
314214
}
315-
}
316-
// conversion cast: turn a single value into a 1-element vector
317-
else if constexpr (auxiliary::IsVector_v<U>)
318-
{
319-
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>)
320217
{
321-
U res{};
322-
res.reserve(1);
323-
res.push_back(static_cast<typename U::value_type>(*pv));
324-
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+
}
325230
}
326231
else
327232
{
328-
return {};
233+
return std::runtime_error("getCast: no cast possible.");
329234
}
330-
}
331-
else
332-
{
333-
return {};
334-
}
335235
#if defined(__INTEL_COMPILER)
336236
/*
337237
* ICPC has trouble with if constexpr, thinking that return statements are
@@ -341,47 +241,58 @@ auto doConvertOptional(T *pv) -> std::optional<U>
341241
* https://community.intel.com/t5/Intel-C-Compiler/quot-if-constexpr-quot-and-quot-missing-return-statement-quot-in/td-p/1154551
342242
*/
343243
#pragma warning(disable : 1011)
344-
}
244+
}
345245
#pragma warning(default : 1011)
346246
#else
347-
}
247+
}
348248
#endif
249+
} // namespace detail
349250

350-
/** Retrieve a stored specific Attribute and cast if convertible.
351-
*
352-
* @throw std::runtime_error if stored object is not static castable to U.
353-
* @tparam U Type of the object to be casted to.
354-
* @return Copy of the retrieved object, casted to type U.
355-
*/
356251
template <typename U>
357-
inline U getCast(Attribute const &a)
252+
U Attribute::get() const
358253
{
359-
auto v = a.getResource();
360-
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());
361260
return std::visit(
362261
[](auto &&containedValue) -> U {
363-
using containedType = std::decay_t<decltype(containedValue)>;
364-
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+
}
365271
},
366-
v);
367-
}
368-
369-
template <typename U>
370-
U Attribute::get() const
371-
{
372-
return getCast<U>(Variant::getResource());
272+
std::move(eitherValueOrError));
373273
}
374274

375275
template <typename U>
376276
std::optional<U> Attribute::getOptional() const
377277
{
378-
auto v = Variant::getResource();
379-
380-
return std::visit(
381-
[](auto &&containedValue) -> U {
278+
auto eitherValueOrError = std::visit(
279+
[](auto &&containedValue) -> std::variant<U, std::runtime_error> {
382280
using containedType = std::decay_t<decltype(containedValue)>;
383-
return doConvert<containedType, U>(&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+
}
384295
},
385-
v);
296+
std::move(eitherValueOrError));
386297
}
387298
} // namespace openPMD

0 commit comments

Comments
 (0)