variant: Add equals operator helper
This adds a generic equality operator intended to make it
straightforward to compare a variant to other variants or base values.
Change-Id: If59296e650bfb43880931e146e4e50b3d8aaa38f
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/include/meson.build b/include/meson.build
index 50ecdef..1402818 100644
--- a/include/meson.build
+++ b/include/meson.build
@@ -20,6 +20,7 @@
'stdplus/str/maps.hpp',
'stdplus/util/cexec.hpp',
'stdplus/util/string.hpp',
+ 'stdplus/variant.hpp',
'stdplus/zstring.hpp',
'stdplus/zstring_view.hpp',
preserve_path: true)
diff --git a/include/stdplus/variant.hpp b/include/stdplus/variant.hpp
new file mode 100644
index 0000000..61d2e27
--- /dev/null
+++ b/include/stdplus/variant.hpp
@@ -0,0 +1,109 @@
+#include <type_traits>
+#include <utility>
+#include <variant>
+
+namespace stdplus
+{
+namespace detail
+{
+
+template <template <typename, typename, typename = void> typename Veq,
+ typename...>
+struct CanVeq;
+
+template <template <typename, typename, typename = void> typename Veq,
+ typename T>
+struct CanVeq<Veq, T>
+{
+ static constexpr inline bool value = false;
+};
+
+template <template <typename, typename, typename = void> typename Veq,
+ typename T, typename V, typename... Vs>
+struct CanVeq<Veq, T, V, Vs...>
+{
+ static constexpr inline bool value = Veq<T, V>::value ||
+ CanVeq<Veq, T, Vs...>::value;
+};
+
+template <typename T1, typename T2>
+struct VeqBase
+{
+ static constexpr inline bool value = false;
+
+ constexpr bool operator()(const T1&, const T2&) const noexcept
+ {
+ return false;
+ }
+};
+
+template <typename T1, typename T2, typename = void>
+struct VeqFuzzy : VeqBase<T1, T2>
+{};
+
+template <typename T1, typename T2>
+struct VeqFuzzy<T1, T2,
+ std::enable_if_t<std::is_same_v<
+ decltype(std::declval<T1>() == std::declval<T2>()), bool>>>
+{
+ static constexpr inline bool value = true;
+
+ constexpr bool operator()(const T1& lhs, const T2& rhs) const noexcept
+ {
+ return lhs == rhs;
+ }
+};
+
+template <typename T1, typename T2, typename = void>
+struct VeqStrict : VeqBase<T1, T2>
+{};
+
+template <typename T1, typename T2>
+struct VeqStrict<T1, T2, std::enable_if_t<std::is_same_v<T1, T2>>>
+{
+ static constexpr inline bool value = true;
+
+ constexpr bool operator()(const T1& lhs, const T2& rhs) const noexcept
+ {
+ return lhs == rhs;
+ }
+};
+
+} // namespace detail
+
+template <template <typename, typename, typename = void> typename Veq,
+ typename... Vs, typename T,
+ std::enable_if_t<detail::CanVeq<Veq, T, Vs...>::value, bool> = true>
+constexpr bool variantEq(const std::variant<Vs...>& vs, const T& t) noexcept
+{
+ return std::visit(
+ [&t](const auto& v) {
+ return Veq<std::remove_cvref_t<decltype(v)>, T>{}(v, t);
+ },
+ vs);
+}
+
+template <template <typename, typename, typename = void> typename Veq,
+ typename... Vs, typename... Vs2>
+constexpr bool variantEq(const std::variant<Vs...>& vs,
+ const std::variant<Vs2...>& vs2) noexcept
+{
+ return std::visit([&vs](const auto& v) { return variantEq<Veq>(vs, v); },
+ vs2);
+}
+
+template <typename... Vs>
+constexpr bool variantEqFuzzy(const std::variant<Vs...>& vs,
+ const auto& t) noexcept
+{
+ return variantEq<detail::VeqFuzzy>(vs, t);
+}
+
+template <typename... Vs>
+constexpr bool variantEqStrict(const std::variant<Vs...>& vs,
+ const auto& t) noexcept
+{
+ return variantEq<detail::VeqStrict>(vs, t);
+}
+
+} // namespace stdplus
diff --git a/src/meson.build b/src/meson.build
index 7367761..dd67b35 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -60,6 +60,7 @@
'str/cexpr.cpp',
'str/maps.cpp',
'util/cexec.cpp',
+ 'variant.cpp',
'zstring.cpp',
'zstring_view.cpp',
]
diff --git a/src/variant.cpp b/src/variant.cpp
new file mode 100644
index 0000000..6af326c
--- /dev/null
+++ b/src/variant.cpp
@@ -0,0 +1 @@
+#include <stdplus/variant.hpp>
diff --git a/test/meson.build b/test/meson.build
index c9ee047..9c0580b 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -16,6 +16,7 @@
'str/cexpr': [stdplus_dep, gtest_main_dep],
'str/maps': [stdplus_dep, gmock_dep, gtest_main_dep],
'util/cexec': [stdplus_dep, gtest_main_dep],
+ 'variant': [stdplus_dep, gtest_main_dep],
'zstring': [stdplus_dep, gtest_main_dep],
'zstring_view': [stdplus_dep, gtest_main_dep],
}
diff --git a/test/variant.cpp b/test/variant.cpp
new file mode 100644
index 0000000..48b4843
--- /dev/null
+++ b/test/variant.cpp
@@ -0,0 +1,41 @@
+#include <stdplus/variant.hpp>
+
+#include <gtest/gtest.h>
+
+namespace stdplus
+{
+
+using namespace std::literals::string_literals;
+using V = std::variant<int, float, std::string>;
+
+TEST(variantEqFuzzy, Basic)
+{
+ EXPECT_TRUE(variantEqFuzzy(V{1}, 1));
+ EXPECT_TRUE(variantEqFuzzy(V{1}, V{1}));
+ EXPECT_TRUE(variantEqFuzzy(V{1}, 1.));
+ EXPECT_TRUE(variantEqFuzzy(V{1}, V{1.f}));
+ EXPECT_TRUE(variantEqFuzzy(V{1.f}, 1));
+ EXPECT_TRUE(variantEqFuzzy(V{1.f}, 1.));
+ EXPECT_TRUE(variantEqFuzzy(V{1.f}, 1.f));
+ EXPECT_FALSE(variantEqFuzzy(V{"1"}, 1));
+ EXPECT_FALSE(variantEqFuzzy(V{"1"}, 1.));
+ EXPECT_TRUE(variantEqFuzzy(V{"1"}, "1"));
+ EXPECT_TRUE(variantEqFuzzy(V{"1"}, V{"1"}));
+ EXPECT_FALSE(variantEqFuzzy(V{"1"}, V{1}));
+}
+
+TEST(variantEqStrict, Basic)
+{
+ EXPECT_TRUE(variantEqStrict(V{1}, 1));
+ EXPECT_TRUE(variantEqStrict(V{1}, V{1}));
+ EXPECT_FALSE(variantEqStrict(V{1}, 1.f));
+ EXPECT_FALSE(variantEqStrict(V{1}, V{1.f}));
+ EXPECT_FALSE(variantEqStrict(V{1.f}, 1));
+ EXPECT_TRUE(variantEqStrict(V{1.f}, 1.f));
+ EXPECT_FALSE(variantEqStrict(V{"1"}, 1));
+ EXPECT_FALSE(variantEqStrict(V{"1"}, 1.f));
+ EXPECT_TRUE(variantEqStrict(V{"1"}, "1"s));
+ EXPECT_TRUE(variantEqStrict(V{"1"}, V{"1"}));
+}
+
+} // namespace stdplus