Skip to content

Commit

Permalink
Merge branch 'add-attribute-getoptional' into topic-json-template-rebase
Browse files Browse the repository at this point in the history
Merge to be removed after rebasing onto openPMD#1278
  • Loading branch information
franzpoeschel committed Jul 29, 2022
2 parents 320a3bc + 4c56fb1 commit 26bbc21
Show file tree
Hide file tree
Showing 10 changed files with 218 additions and 153 deletions.
222 changes: 132 additions & 90 deletions include/openPMD/backend/Attribute.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@
#include <complex>
#include <cstdint>
#include <iterator>
#include <optional>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>

namespace openPMD
Expand Down Expand Up @@ -119,100 +121,117 @@ class Attribute
*/
template <typename U>
U get() const;

/** Retrieve a stored specific Attribute and cast if convertible.
* Like Attribute::get<>(), but returns an empty std::optional if no
* conversion is possible instead of throwing an exception.
*
* @note This performs a static_cast and might introduce precision loss if
* requested. Check dtype explicitly beforehand if needed.
*
* @tparam U Type of the object to be casted to.
* @return Copy of the retrieved object, casted to type U.
* An empty std::optional if no conversion is possible.
*/
template <typename U>
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>)
{
return static_cast<U>(*pv);
}
else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsVector_v<U>)
template <typename T, typename U>
auto doConvert(T *pv) -> std::variant<U, std::runtime_error>
{
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
(void)pv;
if constexpr (std::is_convertible_v<T, U>)
{
throw std::runtime_error("getCast: no vector cast possible.");
return static_cast<U>(*pv);
}
}
// 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>)
else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsVector_v<U>)
{
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.");
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 std::runtime_error("getCast: no vector cast 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>)
// 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>)
{
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>)
{
throw std::runtime_error(
"getCast: no vector to array conversion possible.");
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
{
throw std::runtime_error(
"getCast: no scalar to vector conversion possible.");
return std::runtime_error("getCast: no cast 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
Expand All @@ -222,35 +241,58 @@ auto doConvert(T *pv) -> 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);
std::move(eitherValueOrError));
}

template <typename U>
U Attribute::get() const
std::optional<U> Attribute::getOptional() const
{
return getCast<U>(Variant::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) -> 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)};
}
},
std::move(eitherValueOrError));
}

} // namespace openPMD
30 changes: 10 additions & 20 deletions include/openPMD/backend/BaseRecord.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,24 +317,10 @@ inline void BaseRecord<T_elem>::readBase()
aRead.name = "unitDimension";
this->IOHandler()->enqueue(IOTask(this, aRead));
this->IOHandler()->flush(internal::defaultFlushParams);
if (*aRead.dtype == DT::ARR_DBL_7)
this->setAttribute(
"unitDimension",
Attribute(*aRead.resource).template get<std::array<double, 7> >());
else if (*aRead.dtype == DT::VEC_DOUBLE)
{
auto vec =
Attribute(*aRead.resource).template get<std::vector<double> >();
if (vec.size() == 7)
{
std::array<double, 7> arr;
std::copy(vec.begin(), vec.end(), arr.begin());
this->setAttribute("unitDimension", arr);
}
else
throw std::runtime_error(
"Unexpected Attribute datatype for 'unitDimension'");
}
if (auto val =
Attribute(*aRead.resource).getOptional<std::array<double, 7> >();
val.has_value())
this->setAttribute("unitDimension", val.value());
else
throw std::runtime_error(
"Unexpected Attribute datatype for 'unitDimension'");
Expand All @@ -344,10 +330,14 @@ inline void BaseRecord<T_elem>::readBase()
this->IOHandler()->flush(internal::defaultFlushParams);
if (*aRead.dtype == DT::FLOAT)
this->setAttribute(
"timeOffset", Attribute(*aRead.resource).template get<float>());
"timeOffset", Attribute(*aRead.resource).get<float>());
else if (*aRead.dtype == DT::DOUBLE)
this->setAttribute(
"timeOffset", Attribute(*aRead.resource).template get<double>());
"timeOffset", Attribute(*aRead.resource).get<double>());
// conversion cast if a backend reports an integer type
else if (auto val = Attribute(*aRead.resource).getOptional<double>();
val.has_value())
this->setAttribute("timeOffset", val.value());
else
throw std::runtime_error(
"Unexpected Attribute datatype for 'timeOffset'");
Expand Down
13 changes: 11 additions & 2 deletions src/Iteration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,10 @@ void Iteration::read_impl(std::string const &groupPath)
setDt(Attribute(*aRead.resource).get<double>());
else if (*aRead.dtype == DT::LONG_DOUBLE)
setDt(Attribute(*aRead.resource).get<long double>());
// conversion cast if a backend reports an integer type
else if (auto val = Attribute(*aRead.resource).getOptional<double>();
val.has_value())
setDt(val.value());
else
throw std::runtime_error("Unexpected Attribute datatype for 'dt'");

Expand All @@ -429,14 +433,19 @@ void Iteration::read_impl(std::string const &groupPath)
setTime(Attribute(*aRead.resource).get<double>());
else if (*aRead.dtype == DT::LONG_DOUBLE)
setTime(Attribute(*aRead.resource).get<long double>());
// conversion cast if a backend reports an integer type
else if (auto val = Attribute(*aRead.resource).getOptional<double>();
val.has_value())
setTime(val.value());
else
throw std::runtime_error("Unexpected Attribute datatype for 'time'");

aRead.name = "timeUnitSI";
IOHandler()->enqueue(IOTask(this, aRead));
IOHandler()->flush(internal::defaultFlushParams);
if (*aRead.dtype == DT::DOUBLE)
setTimeUnitSI(Attribute(*aRead.resource).get<double>());
if (auto val = Attribute(*aRead.resource).getOptional<double>();
val.has_value())
setTimeUnitSI(val.value());
else
throw std::runtime_error(
"Unexpected Attribute datatype for 'timeUnitSI'");
Expand Down
15 changes: 10 additions & 5 deletions src/Mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,25 +337,30 @@ void Mesh::read()
else if (
*aRead.dtype == DT::VEC_LONG_DOUBLE || *aRead.dtype == DT::LONG_DOUBLE)
setGridSpacing(a.get<std::vector<long double> >());
// conversion cast if a backend reports an integer type
else if (auto val = a.getOptional<std::vector<double> >(); val.has_value())
setGridSpacing(val.value());
else
throw std::runtime_error(
"Unexpected Attribute datatype for 'gridSpacing'");

aRead.name = "gridGlobalOffset";
IOHandler()->enqueue(IOTask(this, aRead));
IOHandler()->flush(internal::defaultFlushParams);
if (*aRead.dtype == DT::VEC_DOUBLE || *aRead.dtype == DT::DOUBLE)
setGridGlobalOffset(
Attribute(*aRead.resource).get<std::vector<double> >());
if (auto val =
Attribute(*aRead.resource).getOptional<std::vector<double> >();
val.has_value())
setGridGlobalOffset(val.value());
else
throw std::runtime_error(
"Unexpected Attribute datatype for 'gridGlobalOffset'");

aRead.name = "gridUnitSI";
IOHandler()->enqueue(IOTask(this, aRead));
IOHandler()->flush(internal::defaultFlushParams);
if (*aRead.dtype == DT::DOUBLE)
setGridUnitSI(Attribute(*aRead.resource).get<double>());
if (auto val = Attribute(*aRead.resource).getOptional<double>();
val.has_value())
setGridUnitSI(val.value());
else
throw std::runtime_error(
"Unexpected Attribute datatype for 'gridUnitSI'");
Expand Down
Loading

0 comments on commit 26bbc21

Please sign in to comment.