Skip to content

Commit

Permalink
added missing equal_to operators
Browse files Browse the repository at this point in the history
  • Loading branch information
drexlerd committed Feb 14, 2025
1 parent 7a01d92 commit 1aaacae
Show file tree
Hide file tree
Showing 2 changed files with 177 additions and 0 deletions.
127 changes: 127 additions & 0 deletions include/loki/details/utils/equal_to.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,133 @@ struct EqualTo
bool operator()(const T& lhs, const T& rhs) const { return std::equal_to<T>()(lhs, rhs); }
};

template<typename Key, typename Compare, typename Allocator>
struct EqualTo<std::set<Key, Compare, Allocator>>
{
size_t operator()(const std::set<Key, Compare, Allocator>& lhs, const std::set<Key, Compare, Allocator>& rhs) const
{
// Check size first
if (lhs.size() != rhs.size())
{
return false;
}

// Compare each element using loki::EqualTo
return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), loki::EqualTo<Key>());
}
};

template<typename Key, typename T, typename Compare, typename Allocator>
struct EqualTo<std::map<Key, T, Compare, Allocator>>
{
size_t operator()(const std::map<Key, T, Compare, Allocator>& lhs, const std::map<Key, T, Compare, Allocator>& rhs) const
{
// Check if sizes are different
if (lhs.size() != rhs.size())
{
return false;
}

std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), loki::EqualTo<std::pair<Key, T>>());
}
};

template<typename T, typename Allocator>
struct EqualTo<std::vector<T, Allocator>>
{
size_t operator()(const std::vector<T, Allocator>& lhs, const std::vector<T, Allocator>& rhs) const
{
// Check size first
if (lhs.size() != rhs.size())
{
return false;
}

// Compare each element using loki::EqualTo
return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), loki::EqualTo<T>());
}
};

template<typename T1, typename T2>
struct EqualTo<std::pair<T1, T2>>
{
size_t operator()(const std::pair<T1, T2>& lhs, const std::pair<T1, T2>& rhs) const
{
return loki::EqualTo<T1>()(lhs.first, rhs.first) && loki::EqualTo<T1>()(lhs.second, rhs.second);
}
};

template<typename... Ts>
struct EqualTo<std::tuple<Ts...>>
{
size_t operator()(const std::tuple<Ts...>& lhs, const std::tuple<Ts...>& rhs) const
{
return std::apply([&rhs](const Ts&... lhs_args)
{ return std::apply([&lhs_args...](const Ts&... rhs_args) { return (loki::EqualTo<Ts>()(lhs_args, rhs_args) && ...); }, rhs); },
lhs);
}
};

template<typename... Ts>
struct EqualTo<std::variant<Ts...>>
{
size_t operator()(const std::variant<Ts...>& lhs, const std::variant<Ts...>& rhs) const
{
return std::visit(
[](const auto& l, const auto& r)
{
// Check if types match
if constexpr (std::is_same_v<std::decay_t<decltype(l)>, std::decay_t<decltype(r)>>)
{
// Recursively apply loki::EqualTo for matching types
return loki::EqualTo<std::decay_t<decltype(l)>>()(l, r);
}
// Different types are always unequal
return false;
},
lhs,
rhs);
}
};

template<typename T>
struct EqualTo<std::optional<T>>
{
size_t operator()(const std::optional<T>& lhs, const std::optional<T>& rhs) const
{
// Check for presence of values
if (lhs.has_value() != rhs.has_value())
{
return false;
}

// If both are empty, they're equal
if (!lhs.has_value() && !rhs.has_value())
{
return true;
}

// Compare the contained values using loki::EqualTo
return loki::EqualTo<T>()(lhs.value(), rhs.value());
}
};

template<typename T, std::size_t Extent>
struct EqualTo<std::span<T, Extent>>
{
size_t operator()(const std::span<T, Extent>& lhs, const std::span<T, Extent>& rhs) const
{
// Check size first
if (lhs.size() != rhs.size())
{
return false;
}

// Compare each element using loki::EqualTo
return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), loki::EqualTo<T>());
}
};

/// @brief EqualTo specialization for types T that satisfy `HasIdentifyingMembers`.
/// Dereferences the underlying pointer before forwarding the call to the std::equal_to
/// specialization of `IdentifiableMemberProxy` of T to pairwise compare all members.
Expand Down
50 changes: 50 additions & 0 deletions tests/unit/utils/hash.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2023 Dominik Drexler
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

#include <gtest/gtest.h>
#include <loki/details/utils/equal_to.hpp>
#include <loki/details/utils/hash.hpp>
#include <unordered_set>

namespace loki::domain::tests
{

TEST(LokiTests, UtilsHashTest)
{
struct NumericConstraintSplitDistribution
{
size_t num_true_elements;
size_t num_dont_care_elements;

auto identifying_members() const { return std::forward_as_tuple(std::as_const(num_true_elements), std::as_const(num_dont_care_elements)); }
};

struct NumericConstraintSplit
{
NumericConstraintSplitDistribution distribution;

auto identifying_members() const { return std::forward_as_tuple(std::as_const(distribution)); }
};

using Split = std::variant<NumericConstraintSplit, int>;

auto set = std::unordered_set<Split, loki::Hash<Split>, loki::EqualTo<Split>> {};

auto split = NumericConstraintSplit {};
set.insert(split);
}
}

0 comments on commit 1aaacae

Please sign in to comment.