From 587acb304a40059211a449acbb754567fb09d038 Mon Sep 17 00:00:00 2001 From: Razakhel Date: Sun, 3 Nov 2024 14:56:19 +0100 Subject: [PATCH] [Math/Vector] Added structured bindings support for Raz::Vector - Added std::tuple_size, std::tuple_element & get(Vector) --- include/RaZ/Math/Vector.hpp | 40 ++++++++++++++ tests/src/RaZ/Math/Vector.cpp | 98 +++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) diff --git a/include/RaZ/Math/Vector.hpp b/include/RaZ/Math/Vector.hpp index e03bed1a..a97a98cb 100644 --- a/include/RaZ/Math/Vector.hpp +++ b/include/RaZ/Math/Vector.hpp @@ -293,8 +293,48 @@ constexpr Vec3f Z(0.f, 0.f, 1.f); } // namespace Axis +/// Vector element fetching function for a constant lvalue reference. +/// \tparam I Index of the element. +/// \tparam T Type of the vector's data. +/// \tparam Size Vector's size. +/// \param vec Vector to get the element from. +/// \return Constant lvalue reference on the vector's element. +template +constexpr const T& get(const Vector& vec) noexcept { static_assert(I < Size); return vec[I]; } + +/// Vector element fetching function for a non-constant lvalue reference. +/// \tparam I Index of the element. +/// \tparam T Type of the vector's data. +/// \tparam Size Vector's size. +/// \param vec Vector to get the element from. +/// \return Non-constant lvalue reference on the vector's element. +template +constexpr T& get(Vector& vec) noexcept { static_assert(I < Size); return vec[I]; } + +/// Vector element fetching function for a non-constant rvalue reference. +/// \tparam I Index of the element. +/// \tparam T Type of the vector's data. +/// \tparam Size Vector's size. +/// \param vec Vector to get the element from. +/// \return Non-constant rvalue reference on the vector's element. +template +constexpr T&& get(Vector&& vec) noexcept { static_assert(I < Size); return std::move(vec[I]); } + } // namespace Raz +/// Specialization of std::tuple_size for Vector. +/// \tparam T Type of the vector's data. +/// \tparam Size Vector's size. +template +struct std::tuple_size> : std::integral_constant {}; + +/// Specialization of std::tuple_element for Vector. +/// \tparam I Index of the element. +/// \tparam T Type of the vector's data. +/// \tparam Size Vector's size. +template +struct std::tuple_element> { using type = T; }; + /// Specialization of std::hash for Vector. /// \tparam T Type of the vector's data. /// \tparam Size Vector's size. diff --git a/tests/src/RaZ/Math/Vector.cpp b/tests/src/RaZ/Math/Vector.cpp index 3cd307ea..c0de2ea0 100644 --- a/tests/src/RaZ/Math/Vector.cpp +++ b/tests/src/RaZ/Math/Vector.cpp @@ -7,6 +7,7 @@ #include #include +#include namespace { @@ -322,6 +323,103 @@ TEST_CASE("Vector interpolation", "[math]") { CHECK_THAT(vec3d1.lerp(vec3d2, 1.0), IsNearlyEqualToVector(vec3d2, 0.000000000001)); } +TEST_CASE("Vector structured bindings", "[math]") { + static_assert(std::tuple_size_v == 3); + static_assert(std::tuple_size_v == 4); + static_assert(std::tuple_size_v())> == 1); + + static_assert(std::is_same_v, const int>); + static_assert(std::is_same_v, const double>); + static_assert(std::is_same_v())>, bool>); + static_assert(std::is_same_v())>, const bool>); + static_assert(std::is_same_v())>>, const bool>); + + // When using structured bindings, Raz::get(e) is exclusively found using ADL (https://en.cppreference.com/w/cpp/language/adl) + static_assert(Raz::get<0>(vec3f1) == vec3f1[0]); + static_assert(Raz::get<1>(vec3f1) == vec3f1[1]); + static_assert(Raz::get<2>(vec3f1) == vec3f1[2]); + + { + const auto [x, y, z] = vec3b2; + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + + CHECK(x == vec3b2.x()); + CHECK(y == vec3b2.y()); + CHECK(z == vec3b2.z()); + + // Elements are new variables + CHECK_FALSE(&x == &vec3b2.x()); + CHECK_FALSE(&y == &vec3b2.y()); + CHECK_FALSE(&z == &vec3b2.z()); + } + + { + const auto& [x, y, z] = vec3d2; + // decltype-ing a structured binding doesn't show a reference... + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + // ... but getting the elements independently does... + static_assert(std::is_same_v(vec3d2)), const double&>); + static_assert(std::is_same_v(vec3d2)), const double&>); + static_assert(std::is_same_v(vec3d2)), const double&>); + + CHECK(x == vec3d2.x()); + CHECK(y == vec3d2.y()); + CHECK(z == vec3d2.z()); + + // ... and the elements are linked as expected + CHECK(&x == &vec3d2.x()); + CHECK(&y == &vec3d2.y()); + CHECK(&z == &vec3d2.z()); + } + + { + Raz::Vector boolVec(38); + + { + const auto [x] = boolVec; + static_assert(std::is_same_v); + CHECK(x == boolVec.x()); + CHECK_FALSE(&x == &boolVec.x()); + } + + { + auto [x] = boolVec; + static_assert(std::is_same_v); + CHECK(x == boolVec.x()); + CHECK_FALSE(&x == &boolVec.x()); + } + + { + const auto& [x] = boolVec; + static_assert(std::is_same_v); + static_assert(std::is_same_v(std::as_const(boolVec))), const bool&>); + CHECK(x == boolVec.x()); + CHECK(&x == &boolVec.x()); + } + + { + auto& [x] = boolVec; + static_assert(std::is_same_v); + static_assert(std::is_same_v(boolVec)), bool&>); + CHECK(x == boolVec.x()); + CHECK(&x == &boolVec.x()); + } + + { + auto&& [x] = std::move(boolVec); + static_assert(std::is_same_v); + static_assert(std::is_same_v(std::move(boolVec))), bool&&>); // xvalue + static_assert(std::is_same_v(Raz::Vector())), bool&&>); // prvalue + CHECK(x == boolVec.x()); + CHECK(&x == &boolVec.x()); + } + } +} + TEST_CASE("Vector hash", "[math]") { CHECK(vec3b1.hash() == vec3b1.hash()); CHECK_FALSE(vec3b1.hash() == vec3b2.hash());