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