Skip to content

Commit

Permalink
[Math/Vector] Added structured bindings support for Raz::Vector
Browse files Browse the repository at this point in the history
- Added std::tuple_size<Vector>, std::tuple_element<Vector> & get<I>(Vector)
  • Loading branch information
Razakhel committed Nov 3, 2024
1 parent 3e79f46 commit 587acb3
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 0 deletions.
40 changes: 40 additions & 0 deletions include/RaZ/Math/Vector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <std::size_t I, typename T, std::size_t Size>
constexpr const T& get(const Vector<T, Size>& 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 <std::size_t I, typename T, std::size_t Size>
constexpr T& get(Vector<T, Size>& 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 <std::size_t I, typename T, std::size_t Size>
constexpr T&& get(Vector<T, Size>&& 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 <typename T, std::size_t Size>
struct std::tuple_size<Raz::Vector<T, Size>> : std::integral_constant<std::size_t, Size> {};

/// 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 <std::size_t I, typename T, std::size_t Size>
struct std::tuple_element<I, Raz::Vector<T, Size>> { using type = T; };

/// Specialization of std::hash for Vector.
/// \tparam T Type of the vector's data.
/// \tparam Size Vector's size.
Expand Down
98 changes: 98 additions & 0 deletions tests/src/RaZ/Math/Vector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <catch2/catch_test_macros.hpp>

#include <unordered_map>
#include <utility>

namespace {

Expand Down Expand Up @@ -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<decltype(vec3b1)> == 3);
static_assert(std::tuple_size_v<decltype(vec4f1)> == 4);
static_assert(std::tuple_size_v<decltype(Raz::Vector<bool, 1>())> == 1);

static_assert(std::is_same_v<std::tuple_element_t<0, decltype(vec3i1)>, const int>);
static_assert(std::is_same_v<std::tuple_element_t<2, decltype(vec3d1)>, const double>);
static_assert(std::is_same_v<std::tuple_element_t<0, decltype(Raz::Vector<bool, 1>())>, bool>);
static_assert(std::is_same_v<std::tuple_element_t<0, decltype(Raz::Vector<const bool, 1>())>, const bool>);
static_assert(std::is_same_v<std::tuple_element_t<0, std::add_const_t<decltype(Raz::Vector<bool, 1>())>>, const bool>);

// When using structured bindings, Raz::get<I>(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<decltype(x), const uint8_t>);
static_assert(std::is_same_v<decltype(y), const uint8_t>);
static_assert(std::is_same_v<decltype(z), const uint8_t>);

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<decltype(x), const double>);
static_assert(std::is_same_v<decltype(y), const double>);
static_assert(std::is_same_v<decltype(z), const double>);
// ... but getting the elements independently does...
static_assert(std::is_same_v<decltype(Raz::get<0>(vec3d2)), const double&>);
static_assert(std::is_same_v<decltype(Raz::get<1>(vec3d2)), const double&>);
static_assert(std::is_same_v<decltype(Raz::get<2>(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<bool, 1> boolVec(38);

{
const auto [x] = boolVec;
static_assert(std::is_same_v<decltype(x), const bool>);
CHECK(x == boolVec.x());
CHECK_FALSE(&x == &boolVec.x());
}

{
auto [x] = boolVec;
static_assert(std::is_same_v<decltype(x), bool>);
CHECK(x == boolVec.x());
CHECK_FALSE(&x == &boolVec.x());
}

{
const auto& [x] = boolVec;
static_assert(std::is_same_v<decltype(x), const bool>);
static_assert(std::is_same_v<decltype(Raz::get<0>(std::as_const(boolVec))), const bool&>);
CHECK(x == boolVec.x());
CHECK(&x == &boolVec.x());
}

{
auto& [x] = boolVec;
static_assert(std::is_same_v<decltype(x), bool>);
static_assert(std::is_same_v<decltype(Raz::get<0>(boolVec)), bool&>);
CHECK(x == boolVec.x());
CHECK(&x == &boolVec.x());
}

{
auto&& [x] = std::move(boolVec);
static_assert(std::is_same_v<decltype(x), bool>);
static_assert(std::is_same_v<decltype(Raz::get<0>(std::move(boolVec))), bool&&>); // xvalue
static_assert(std::is_same_v<decltype(Raz::get<0>(Raz::Vector<bool, 1>())), 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());
Expand Down

0 comments on commit 587acb3

Please sign in to comment.