diff --git a/include/openPMD/backend/Attribute.hpp b/include/openPMD/backend/Attribute.hpp index 900d807be0..5471283662 100644 --- a/include/openPMD/backend/Attribute.hpp +++ b/include/openPMD/backend/Attribute.hpp @@ -98,8 +98,7 @@ struct DoConvert; template< typename T, typename U > struct DoConvert { - template< typename PV > - U operator()( PV ) + U operator()( T * ) { throw std::runtime_error("getCast: no cast possible."); } @@ -108,8 +107,7 @@ struct DoConvert template< typename T, typename U > struct DoConvert { - template< typename PV > - U operator()( PV pv ) + U operator()( T * pv ) { return static_cast< U >( *pv ); } @@ -120,8 +118,8 @@ struct DoConvert, std::vector< U >, false> { static constexpr bool convertible = std::is_convertible::value; - template< typename PV, typename UU = U > - auto operator()( PV pv ) + template< typename UU = U > + auto operator()( std::vector< T > const * pv ) -> typename std::enable_if< convertible, std::vector< UU > >::type { std::vector< U > u; @@ -130,14 +128,101 @@ struct DoConvert, std::vector< U >, false> return u; } - template< typename PV, typename UU = U > - auto operator()( PV ) + template< typename UU = U > + auto operator()( std::vector< T > const * ) -> typename std::enable_if< !convertible, std::vector< UU > >::type { throw std::runtime_error("getCast: no vector cast possible."); } }; +// conversion cast: turn a single value into a 1-element vector +template< typename T, typename U > +struct DoConvert, false> +{ + static constexpr bool convertible = std::is_convertible::value; + + template< typename UU = U > + auto operator()( T const * pv ) + -> typename std::enable_if< convertible, std::vector< UU > >::type + { + std::vector< U > u; + u.reserve( 1 ); + u.push_back( static_cast< U >( *pv ) ); + return u; + } + + template< typename UU = U > + auto operator()( T const * ) + -> typename std::enable_if< !convertible, std::vector< UU > >::type + { + throw std::runtime_error( + "getCast: no scalar to vector conversion possible."); + } +}; + +// conversion cast: array to vector +// if a backend reports a std::array<> for something where the frontend expects +// a vector +template< typename T, typename U, size_t n > +struct DoConvert, std::vector< U >, false> +{ + static constexpr bool convertible = std::is_convertible::value; + + template< typename UU = U > + auto operator()( std::array< T, n > const * pv ) + -> typename std::enable_if< convertible, std::vector< UU > >::type + { + std::vector< U > u; + u.reserve( n ); + std::copy( pv->begin(), pv->end(), std::back_inserter(u) ); + return u; + } + + template< typename UU = U > + auto operator()( std::array< T, n > const * ) + -> typename std::enable_if< !convertible, std::vector< UU > >::type + { + 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 +template< typename T, typename U, size_t n > +struct DoConvert, std::array< U, n >, false> +{ + static constexpr bool convertible = std::is_convertible::value; + + template< typename UU = U > + auto operator()( std::vector< T > const * pv ) + -> typename std::enable_if< convertible, std::array< UU, n > >::type + { + std::array< U, n > u; + if( n != pv->size() ) + { + throw std::runtime_error( + "getCast: no vector to array conversion possible " + "(wrong requested array size)."); + } + for( size_t i = 0; i < n; ++i ) + { + u[ i ] = static_cast< U >( ( *pv )[ i ] ); + } + return u; + } + + template< typename UU = U > + auto operator()( std::vector< T > const * ) + -> typename std::enable_if< !convertible, std::array< UU, n > >::type + { + throw std::runtime_error( + "getCast: no vector to array conversion possible."); + } +}; + /** Retrieve a stored specific Attribute and cast if convertible. * * @throw std::runtime_error if stored object is not static castable to U. @@ -150,6 +235,12 @@ getCast( Attribute const & a ) { auto v = a.getResource(); + // icpc 2021.3.0 does not like variantSrc::visit (with mpark-variant) + // we use variantSrc::visit for the other compilers to avoid having an + // endless list of if-then-else + // also, once we switch to C++17, we might throw this out in + // favor of a hopefully working std::visit +#if defined(__ICC) || defined(__INTEL_COMPILER) if(auto pvalue_c = variantSrc::get_if< char >( &v ) ) return DoConvert{}(pvalue_c); else if(auto pvalue_uc = variantSrc::get_if< unsigned char >( &v ) ) @@ -226,6 +317,15 @@ getCast( Attribute const & a ) return DoConvert{}(pvalue_b); else throw std::runtime_error("getCast: unknown Datatype."); + +#else + return variantSrc::visit( + []( auto && containedValue ) -> U { + using containedType = std::decay_t< decltype( containedValue ) >; + return DoConvert< containedType, U >{}( &containedValue ); + }, + v ); +#endif } template< typename U > diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 5d8ebe439d..e9551c42e6 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -318,10 +318,8 @@ Mesh::read() aRead.name = "axisLabels"; IOHandler()->enqueue(IOTask(this, aRead)); IOHandler()->flush(); - if( *aRead.dtype == DT::VEC_STRING ) + if( *aRead.dtype == DT::VEC_STRING || *aRead.dtype == DT::STRING) setAxisLabels(Attribute(*aRead.resource).get< std::vector< std::string > >()); - else if( *aRead.dtype == DT::STRING ) - setAxisLabels({Attribute(*aRead.resource).get< std::string >()}); else throw std::runtime_error("Unexpected Attribute datatype for 'axisLabels'"); @@ -329,24 +327,18 @@ Mesh::read() IOHandler()->enqueue(IOTask(this, aRead)); IOHandler()->flush(); Attribute a = Attribute(*aRead.resource); - if( *aRead.dtype == DT::VEC_FLOAT ) + if( *aRead.dtype == DT::VEC_FLOAT || *aRead.dtype == DT::FLOAT ) setGridSpacing(a.get< std::vector< float > >()); - else if( *aRead.dtype == DT::FLOAT ) - setGridSpacing(std::vector< float >({a.get< float >()})); - else if( *aRead.dtype == DT::VEC_DOUBLE ) + else if( *aRead.dtype == DT::VEC_DOUBLE || *aRead.dtype == DT::DOUBLE ) setGridSpacing(a.get< std::vector< double > >()); - else if( *aRead.dtype == DT::DOUBLE ) - setGridSpacing(std::vector< double >({a.get< double >()})); else throw std::runtime_error("Unexpected Attribute datatype for 'gridSpacing'"); aRead.name = "gridGlobalOffset"; IOHandler()->enqueue(IOTask(this, aRead)); IOHandler()->flush(); - if( *aRead.dtype == DT::VEC_DOUBLE ) + if( *aRead.dtype == DT::VEC_DOUBLE || *aRead.dtype == DT::DOUBLE ) setGridGlobalOffset(Attribute(*aRead.resource).get< std::vector< double > >()); - else if( *aRead.dtype == DT::DOUBLE ) - setGridGlobalOffset({Attribute(*aRead.resource).get< double >()}); else throw std::runtime_error("Unexpected Attribute datatype for 'gridGlobalOffset'"); diff --git a/src/RecordComponent.cpp b/src/RecordComponent.cpp index bff6286e0e..d40061c198 100644 --- a/src/RecordComponent.cpp +++ b/src/RecordComponent.cpp @@ -343,9 +343,8 @@ RecordComponent::readBase() // uint64_t check Datatype const attrDtype = *aRead.dtype; - if( isSame( attrDtype, determineDatatype< uint64_t >() ) ) - e.push_back( a.get< uint64_t >() ); - else if( isSame( attrDtype, determineDatatype< std::vector< uint64_t > >() ) ) + if( isSame( attrDtype, determineDatatype< std::vector< uint64_t > >() ) + || isSame( attrDtype, determineDatatype< uint64_t >() ) ) for( auto const& val : a.get< std::vector< uint64_t > >() ) e.push_back( val ); else diff --git a/src/backend/MeshRecordComponent.cpp b/src/backend/MeshRecordComponent.cpp index 0a3a72d597..8c9634a32c 100644 --- a/src/backend/MeshRecordComponent.cpp +++ b/src/backend/MeshRecordComponent.cpp @@ -39,18 +39,12 @@ MeshRecordComponent::read() IOHandler()->enqueue(IOTask(this, aRead)); IOHandler()->flush(); Attribute a = Attribute(*aRead.resource); - if( *aRead.dtype == DT::VEC_FLOAT ) + if( *aRead.dtype == DT::VEC_FLOAT || *aRead.dtype == DT::FLOAT ) setPosition(a.get< std::vector< float > >()); - else if( *aRead.dtype == DT::FLOAT ) - setPosition(std::vector< float >({a.get< float >()})); - else if( *aRead.dtype == DT::VEC_DOUBLE ) + else if( *aRead.dtype == DT::VEC_DOUBLE || *aRead.dtype == DT::DOUBLE ) setPosition(a.get< std::vector< double > >()); - else if( *aRead.dtype == DT::DOUBLE ) - setPosition(std::vector< double >({a.get< double >()})); - else if( *aRead.dtype == DT::VEC_LONG_DOUBLE ) + else if( *aRead.dtype == DT::VEC_LONG_DOUBLE || *aRead.dtype == DT::LONG_DOUBLE ) setPosition(a.get< std::vector< long double > >()); - else if( *aRead.dtype == DT::LONG_DOUBLE ) - setPosition(std::vector< long double >({a.get< long double >()})); else throw std::runtime_error( "Unexpected Attribute datatype for 'position'"); diff --git a/src/backend/PatchRecord.cpp b/src/backend/PatchRecord.cpp index 9179c03c78..3926677a30 100644 --- a/src/backend/PatchRecord.cpp +++ b/src/backend/PatchRecord.cpp @@ -62,21 +62,8 @@ PatchRecord::read() IOHandler()->enqueue(IOTask(this, aRead)); IOHandler()->flush(); - if( *aRead.dtype == Datatype::ARR_DBL_7 ) + if( *aRead.dtype == Datatype::ARR_DBL_7 || *aRead.dtype == Datatype::VEC_DOUBLE ) this->setAttribute("unitDimension", Attribute(*aRead.resource).template get< std::array< double, 7 > >()); - else if( *aRead.dtype == Datatype::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'"); - } else throw std::runtime_error("Unexpected Attribute datatype for 'unitDimension'"); diff --git a/test/CoreTest.cpp b/test/CoreTest.cpp index 8e5b2c9937..72830b0df2 100644 --- a/test/CoreTest.cpp +++ b/test/CoreTest.cpp @@ -950,3 +950,73 @@ TEST_CASE( "load_chunk_wrong_datatype", "[core]" ) "Type conversion during chunk loading not yet implemented" ) ); } } + +TEST_CASE( "DoConvert_single_value_to_vector", "[core]" ) +{ +#if openPMD_HAVE_ADIOS2 + { + Series write( "../samples/writeSingleMesh.bp", Access::CREATE ); + auto E_x = write.iterations[ 0 ].meshes[ "E" ][ "x" ]; + E_x.resetDataset( { Datatype::INT, { 10 } } ); + E_x.makeConstant( 10 ); + } + { + Series read( "../samples/writeSingleMesh.bp", Access::READ_ONLY ); + auto E = read.iterations[ 0 ].meshes[ "E" ]; + REQUIRE( E.axisLabels() == std::vector< std::string >{ "x" } ); + } +#endif + { + char val = 'x'; + Attribute attr{ val }; + + // the following conversions should be possible + REQUIRE( attr.get< char >() == 'x' ); // no conversion + REQUIRE( attr.get< unsigned char >() == 'x' ); + REQUIRE( attr.get< signed char >() == 'x' ); + // all the previous ones, but make them single-element vectors now + REQUIRE( + attr.get< std::vector< char > >() == std::vector< char >{ 'x' } ); + REQUIRE( + attr.get< std::vector< unsigned char > >() == + std::vector< unsigned char >{ 'x' } ); + REQUIRE( + attr.get< std::vector< signed char > >() == + std::vector< signed char >{ 'x' } ); + } + { + std::array< double, 7 > array{{ 0, 1, 2, 3, 4, 5, 6 }}; + Attribute attr{ array }; + + // the following conversions should be possible + REQUIRE( attr.get< std::array< double, 7 > >() == array ); + // we don't need array-to-array conversions, + // so array< int, 7 > cannot be loaded here + REQUIRE( + attr.get< std::vector< double > >() == + std::vector< double >{ 0, 1, 2, 3, 4, 5, 6 } ); + REQUIRE( + attr.get< std::vector< int > >() == + std::vector< int >{ 0, 1, 2, 3, 4, 5, 6 } ); + } + { + std::vector< double > vector{ 0, 1, 2, 3, 4, 5, 6 }; + std::array< double, 7 > arraydouble{{ 0, 1, 2, 3, 4, 5, 6 }}; + std::array< int, 7 > arrayint{{ 0, 1, 2, 3, 4, 5, 6 }}; + Attribute attr{ vector }; + + // the following conversions should be possible + REQUIRE( attr.get< std::array< double, 7 > >() == arraydouble ); + REQUIRE( attr.get< std::array< int, 7 > >() == arrayint ); + REQUIRE_THROWS_WITH( + ( attr.get< std::array< int, 8 > >() ), + Catch::Equals( "getCast: no vector to array conversion possible " + "(wrong requested array size)." ) ); + REQUIRE( + attr.get< std::vector< double > >() == + std::vector< double >{ 0, 1, 2, 3, 4, 5, 6 } ); + REQUIRE( + attr.get< std::vector< int > >() == + std::vector< int >{ 0, 1, 2, 3, 4, 5, 6 } ); + } +}