Skip to content

Commit

Permalink
Unify code paths for both get() and getOptional()
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
franzpoeschel committed May 30, 2022
1 parent 2bb99dd commit 0808dec
Showing 1 changed file with 110 additions and 199 deletions.
309 changes: 110 additions & 199 deletions include/openPMD/backend/Attribute.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <string>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>

namespace openPMD
Expand Down Expand Up @@ -134,202 +135,101 @@ class Attribute
std::optional<U> getOptional() const;
};

template <typename T, typename U>
auto doConvert(T *pv) -> U
namespace detail
{
(void)pv;
if constexpr (std::is_convertible_v<T, U>)
template <typename T, typename U>
auto doConvert(T *pv) -> std::variant<U, std::runtime_error>
{
return static_cast<U>(*pv);
}
else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsVector_v<U>)
{
if constexpr (std::is_convertible_v<
typename T::value_type,
typename U::value_type>)
(void)pv;
if constexpr (std::is_convertible_v<T, U>)
{
U res{};
res.reserve(pv->size());
std::copy(pv->begin(), pv->end(), std::back_inserter(res));
return res;
return static_cast<U>(*pv);
}
else
{
throw std::runtime_error("getCast: no vector cast possible.");
}
}
// conversion cast: array to vector
// if a backend reports a std::array<> for something where
// the frontend expects a vector
else if constexpr (auxiliary::IsArray_v<T> && auxiliary::IsVector_v<U>)
{
if constexpr (std::is_convertible_v<
typename T::value_type,
typename U::value_type>)
{
U res{};
res.reserve(pv->size());
std::copy(pv->begin(), pv->end(), std::back_inserter(res));
return res;
}
else
{
throw std::runtime_error(
"getCast: no array to vector conversion possible.");
}
}
// conversion cast: vector to array
// if a backend reports a std::vector<> for something where
// the frontend expects an array
else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsArray_v<U>)
{
if constexpr (std::is_convertible_v<
typename T::value_type,
typename U::value_type>)
else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsVector_v<U>)
{
U res{};
if (res.size() != pv->size())
if constexpr (std::is_convertible_v<
typename T::value_type,
typename U::value_type>)
{
throw std::runtime_error(
"getCast: no vector to array conversion possible (wrong "
"requested array size).");
U res{};
res.reserve(pv->size());
std::copy(pv->begin(), pv->end(), std::back_inserter(res));
return res;
}
for (size_t i = 0; i < res.size(); ++i)
else
{
res[i] = static_cast<typename U::value_type>((*pv)[i]);
return std::runtime_error("getCast: no vector cast possible.");
}
return res;
}
else
{
throw std::runtime_error(
"getCast: no vector to array conversion possible.");
}
}
// conversion cast: turn a single value into a 1-element vector
else if constexpr (auxiliary::IsVector_v<U>)
{
if constexpr (std::is_convertible_v<T, typename U::value_type>)
{
U res{};
res.reserve(1);
res.push_back(static_cast<typename U::value_type>(*pv));
return res;
}
else
{
throw std::runtime_error(
"getCast: no scalar to vector conversion possible.");
}
}
else
{
throw std::runtime_error("getCast: no cast possible.");
}
#if defined(__INTEL_COMPILER)
/*
* ICPC has trouble with if constexpr, thinking that return statements are
* missing afterwards. Deactivate the warning.
* Note that putting a statement here will not help to fix this since it will
* then complain about unreachable code.
* https://community.intel.com/t5/Intel-C-Compiler/quot-if-constexpr-quot-and-quot-missing-return-statement-quot-in/td-p/1154551
*/
#pragma warning(disable : 1011)
}
#pragma warning(default : 1011)
#else
}
#endif

template <typename T, typename U>
auto doConvertOptional(T *pv) -> std::optional<U>
{
(void)pv;
if constexpr (std::is_convertible_v<T, U>)
{
return static_cast<U>(*pv);
}
else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsVector_v<U>)
{
if constexpr (std::is_convertible_v<
typename T::value_type,
typename U::value_type>)
{
U res{};
res.reserve(pv->size());
std::copy(pv->begin(), pv->end(), std::back_inserter(res));
return res;
}
else
// conversion cast: array to vector
// if a backend reports a std::array<> for something where
// the frontend expects a vector
else if constexpr (auxiliary::IsArray_v<T> && auxiliary::IsVector_v<U>)
{
return {};
}
}
// conversion cast: array to vector
// if a backend reports a std::array<> for something where
// the frontend expects a vector
else if constexpr (auxiliary::IsArray_v<T> && auxiliary::IsVector_v<U>)
{
if constexpr (std::is_convertible_v<
typename T::value_type,
typename U::value_type>)
{
U res{};
res.reserve(pv->size());
std::copy(pv->begin(), pv->end(), std::back_inserter(res));
return res;
}
else
{
return {};
}
}
// conversion cast: vector to array
// if a backend reports a std::vector<> for something where
// the frontend expects an array
else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsArray_v<U>)
{
if constexpr (std::is_convertible_v<
typename T::value_type,
typename U::value_type>)
{
U res{};
if (res.size() != pv->size())
if constexpr (std::is_convertible_v<
typename T::value_type,
typename U::value_type>)
{
throw std::runtime_error(
"getCast: no vector to array conversion possible (wrong "
"requested array size).");
U res{};
res.reserve(pv->size());
std::copy(pv->begin(), pv->end(), std::back_inserter(res));
return res;
}
for (size_t i = 0; i < res.size(); ++i)
else
{
res[i] = static_cast<typename U::value_type>((*pv)[i]);
return std::runtime_error(
"getCast: no array to vector conversion possible.");
}
return res;
}
else
// conversion cast: vector to array
// if a backend reports a std::vector<> for something where
// the frontend expects an array
else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsArray_v<U>)
{
return {};
if constexpr (std::is_convertible_v<
typename T::value_type,
typename U::value_type>)
{
U res{};
if (res.size() != pv->size())
{
return std::runtime_error(
"getCast: no vector to array conversion possible "
"(wrong "
"requested array size).");
}
for (size_t i = 0; i < res.size(); ++i)
{
res[i] = static_cast<typename U::value_type>((*pv)[i]);
}
return res;
}
else
{
return std::runtime_error(
"getCast: no vector to array conversion possible.");
}
}
}
// conversion cast: turn a single value into a 1-element vector
else if constexpr (auxiliary::IsVector_v<U>)
{
if constexpr (std::is_convertible_v<T, typename U::value_type>)
// conversion cast: turn a single value into a 1-element vector
else if constexpr (auxiliary::IsVector_v<U>)
{
U res{};
res.reserve(1);
res.push_back(static_cast<typename U::value_type>(*pv));
return res;
if constexpr (std::is_convertible_v<T, typename U::value_type>)
{
U res{};
res.reserve(1);
res.push_back(static_cast<typename U::value_type>(*pv));
return res;
}
else
{
return std::runtime_error(
"getCast: no scalar to vector conversion possible.");
}
}
else
{
return {};
return std::runtime_error("getCast: no cast possible.");
}
}
else
{
return {};
}
#if defined(__INTEL_COMPILER)
/*
* ICPC has trouble with if constexpr, thinking that return statements are
Expand All @@ -339,47 +239,58 @@ auto doConvertOptional(T *pv) -> std::optional<U>
* https://community.intel.com/t5/Intel-C-Compiler/quot-if-constexpr-quot-and-quot-missing-return-statement-quot-in/td-p/1154551
*/
#pragma warning(disable : 1011)
}
}
#pragma warning(default : 1011)
#else
}
}
#endif
} // namespace detail

/** Retrieve a stored specific Attribute and cast if convertible.
*
* @throw std::runtime_error if stored object is not static castable to U.
* @tparam U Type of the object to be casted to.
* @return Copy of the retrieved object, casted to type U.
*/
template <typename U>
inline U getCast(Attribute const &a)
U Attribute::get() const
{
auto v = a.getResource();

auto eitherValueOrError = std::visit(
[](auto &&containedValue) -> std::variant<U, std::runtime_error> {
using containedType = std::decay_t<decltype(containedValue)>;
return detail::doConvert<containedType, U>(&containedValue);
},
Variant::getResource());
return std::visit(
[](auto &&containedValue) -> U {
using containedType = std::decay_t<decltype(containedValue)>;
return doConvert<containedType, U>(&containedValue);
using T = std::decay_t<decltype(containedValue)>;
if constexpr (std::is_same_v<T, std::runtime_error>)
{
throw std::move(containedValue);
}
else
{
return std::move(containedValue);
}
},
v);
}

template <typename U>
U Attribute::get() const
{
return getCast<U>(Variant::getResource());
std::move(eitherValueOrError));
}

template <typename U>
std::optional<U> Attribute::getOptional() const
{
auto v = Variant::getResource();

return std::visit(
[](auto &&containedValue) -> U {
auto eitherValueOrError = std::visit(
[](auto &&containedValue) -> std::variant<U, std::runtime_error> {
using containedType = std::decay_t<decltype(containedValue)>;
return doConvert<containedType, U>(&containedValue);
return detail::doConvert<containedType, U>(&containedValue);
},
Variant::getResource());
return std::visit(
[](auto &&containedValue) -> std::optional<U> {
using T = std::decay_t<decltype(containedValue)>;
if constexpr (std::is_same_v<T, std::runtime_error>)
{
return std::nullopt;
}
else
{
return {std::move(containedValue)};
}
},
v);
std::move(eitherValueOrError));
}
} // namespace openPMD

0 comments on commit 0808dec

Please sign in to comment.