From 4d7fa4e0135b43747a6fca54c6065d6d6ebb31ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 28 Dec 2022 15:32:49 +0100 Subject: [PATCH] Containers: add explicit StridedDimension conversion from/to ext types. In addition to the implicit conversion. Desired use case is for types where we still want to have a conversion for convenience purposes, but the conversion is potentially dangerous (such as Magnum's Vector3i -> Size3D). --- src/Corrade/Containers/StridedDimensions.h | 27 +++++++- .../Containers/Test/StridedDimensionsTest.cpp | 65 ++++++++++++++++--- 2 files changed, 81 insertions(+), 11 deletions(-) diff --git a/src/Corrade/Containers/StridedDimensions.h b/src/Corrade/Containers/StridedDimensions.h index 0e35f2cdb..11b69821b 100644 --- a/src/Corrade/Containers/StridedDimensions.h +++ b/src/Corrade/Containers/StridedDimensions.h @@ -41,6 +41,7 @@ namespace Corrade { namespace Containers { namespace Implementation { template struct StridedDimensionsConverter; + template struct ExplicitStridedDimensionsConverter; /* Used by StridedArrayView, needed here to friend them */ template struct StridedElement; template struct ArrayCastFlattenOrInflate; @@ -87,7 +88,17 @@ template class StridedDimensions { * Conversion from a @ref StaticArrayView of the same type and * dimension count is builtin. */ - template::type>::from(std::declval()))> constexpr /*implicit*/ StridedDimensions(U&& other) noexcept: StridedDimensions{Implementation::StridedDimensionsConverter::type>::from(Utility::forward(other))} {} + template::type>::from(std::declval()))) != 0, int>::type = 0> constexpr /*implicit*/ StridedDimensions(U&& other) noexcept: StridedDimensions{Implementation::StridedDimensionsConverter::type>::from(Utility::forward(other))} {} + + /** + * @brief Explicitly construct from an external representation + * @m_since_latest + * + * Compared to the above, this conversion is explicit and thus meant to + * be used in scenarios with a potential for data loss, such as + * conversion between signed and unsigned types. + */ + template::type>::from(std::declval()))) != 0, int>::type = 0> constexpr explicit StridedDimensions(U&& other) noexcept: StridedDimensions{Implementation::ExplicitStridedDimensionsConverter::type>::from(Utility::forward(other))} {} /** * @brief Convert to an external representation @@ -96,10 +107,22 @@ template class StridedDimensions { * Conversion to a @ref StaticArrayView of the same type and dimension * count is builtin. */ - template::to(std::declval>()))> constexpr /*implicit*/ operator U() const noexcept { + template::to(std::declval>()))) != 0, int>::type = 0> constexpr /*implicit*/ operator U() const noexcept { return Implementation::StridedDimensionsConverter::to(*this); } + /** + * @brief Explicitly convert to an external representation + * @m_since_latest + * + * Compared to the above, this conversion is explicit and thus meant to + * be used in scenarios with a potential for data loss, such as + * conversion between signed and unsigned types. + */ + template::to(std::declval>()))) != 0, int>::type = 0> constexpr explicit operator U() const noexcept { + return Implementation::ExplicitStridedDimensionsConverter::to(*this); + } + /** * @brief Conversion to a scalar * diff --git a/src/Corrade/Containers/Test/StridedDimensionsTest.cpp b/src/Corrade/Containers/Test/StridedDimensionsTest.cpp index 4c0f005b7..ff940abd4 100644 --- a/src/Corrade/Containers/Test/StridedDimensionsTest.cpp +++ b/src/Corrade/Containers/Test/StridedDimensionsTest.cpp @@ -33,8 +33,15 @@ namespace { -struct Rectangle { - constexpr Rectangle(int rows, int cols): rows{rows}, cols{cols} {} +struct Rectangleu { + constexpr Rectangleu(unsigned rows, unsigned cols): rows{rows}, cols{cols} {} + + unsigned rows; + unsigned cols; +}; + +struct Rectanglei { + constexpr Rectanglei(int rows, int cols): rows{rows}, cols{cols} {} int rows; int cols; @@ -46,11 +53,20 @@ namespace Corrade { namespace Containers { namespace Implementation { -template<> struct StridedDimensionsConverter<2, std::size_t, Rectangle> { - constexpr static Size2D from(const Rectangle& other) { +template<> struct StridedDimensionsConverter<2, std::size_t, Rectangleu> { + constexpr static Size2D from(const Rectangleu& other) { return {std::size_t(other.rows), std::size_t(other.cols)}; } - constexpr static Rectangle to(const Size2D& to) { + constexpr static Rectangleu to(const Size2D& to) { + return {unsigned(to[0]), unsigned(to[1])}; + } +}; + +template<> struct ExplicitStridedDimensionsConverter<2, std::size_t, Rectanglei> { + constexpr static Size2D from(const Rectanglei& other) { + return {std::size_t(other.rows), std::size_t(other.cols)}; + } + constexpr static Rectanglei to(const Size2D& to) { return {int(to[0]), int(to[1])}; } }; @@ -71,6 +87,7 @@ struct StridedDimensionsTest: TestSuite::Tester { void convertScalar(); void convertScalar3D(); void convertExternal(); + void convertExternalExplicit(); void convertExternalStaticArrayView(); void compare(); @@ -90,6 +107,7 @@ StridedDimensionsTest::StridedDimensionsTest() { &StridedDimensionsTest::convertScalar, &StridedDimensionsTest::convertScalar3D, &StridedDimensionsTest::convertExternal, + &StridedDimensionsTest::convertExternalExplicit, &StridedDimensionsTest::convertExternalStaticArrayView, &StridedDimensionsTest::compare, @@ -231,7 +249,7 @@ constexpr Size2D Sizes{34, 67}; void StridedDimensionsTest::convertExternal() { Size2D a{12, 37}; - Rectangle b = a; + Rectangleu b = a; CORRADE_COMPARE(b.rows, 12); CORRADE_COMPARE(b.cols, 37); @@ -239,7 +257,7 @@ void StridedDimensionsTest::convertExternal() { CORRADE_COMPARE(c[0], 12); CORRADE_COMPARE(c[1], 37); - constexpr Rectangle cb = Sizes; + constexpr Rectangleu cb = Sizes; CORRADE_COMPARE(cb.rows, 34); CORRADE_COMPARE(cb.cols, 67); @@ -247,8 +265,37 @@ void StridedDimensionsTest::convertExternal() { CORRADE_COMPARE(cc[0], 34); CORRADE_COMPARE(cc[1], 67); - CORRADE_VERIFY(std::is_nothrow_constructible::value); - CORRADE_VERIFY(std::is_nothrow_constructible::value); + CORRADE_VERIFY(std::is_nothrow_constructible::value); + CORRADE_VERIFY(std::is_nothrow_constructible::value); +} + +void StridedDimensionsTest::convertExternalExplicit() { + Size2D a{12, 37}; + + Rectanglei b{a}; + CORRADE_COMPARE(b.rows, 12); + CORRADE_COMPARE(b.cols, 37); + + Size2D c{b}; + CORRADE_COMPARE(c[0], 12); + CORRADE_COMPARE(c[1], 37); + + constexpr Rectanglei cb{Sizes}; + CORRADE_COMPARE(cb.rows, 34); + CORRADE_COMPARE(cb.cols, 67); + + constexpr Size2D cc{cb}; + CORRADE_COMPARE(cc[0], 34); + CORRADE_COMPARE(cc[1], 67); + + /* Shouldn't be implicitly convertible */ + CORRADE_VERIFY(std::is_convertible::value); + CORRADE_VERIFY(std::is_convertible::value); + CORRADE_VERIFY(!std::is_convertible::value); + CORRADE_VERIFY(!std::is_convertible::value); + + CORRADE_VERIFY(std::is_nothrow_constructible::value); + CORRADE_VERIFY(std::is_nothrow_constructible::value); } void StridedDimensionsTest::convertExternalStaticArrayView() {