utility: add merge_variants_t

This type is useful for processing D-Bus messages containing information
for multiple interfaces. For doing sdbusplus::message::message::read()
on them one needs to define an std::variant which would be a combination
of all the PropertiesVariant's involved.

Signed-off-by: Paul Fertser <fercerpav@gmail.com>
Change-Id: Idcb7b9841cc0253f2f7d5552dae540fba5ad488d
diff --git a/include/sdbusplus/utility/merge_variants.hpp b/include/sdbusplus/utility/merge_variants.hpp
new file mode 100644
index 0000000..89c5571
--- /dev/null
+++ b/include/sdbusplus/utility/merge_variants.hpp
@@ -0,0 +1,59 @@
+#pragma once
+#include <sdbusplus/utility/dedup_variant.hpp>
+
+#include <variant>
+
+/** See `sdbusplus::utility::merge_variants` below. */
+
+namespace sdbusplus
+{
+namespace utility
+{
+
+namespace details
+{
+
+/** Find the deduplicated variant type.
+ *
+ *  @tparam T - A type of the form 'variant<...>'.
+ *  @tparam Unused - An empty type set (non-empty sets are matched in
+ *                   specialization).
+ *
+ *  This template is only matched when Unused is empty, which means all
+ *  types have been processed (merged).
+ */
+template <typename T, typename... Unused>
+struct merge_variants
+{
+    using type = T;
+};
+
+/** Compute the merged variant type.
+ *
+ * @tparam D, Done - Head and tail of the alternative types list of the
+ * first variant.
+ * @tparam Next - The types of the next variant<...> to be merged with the
+ * first.
+ * @tparam Rest - The remaining variants to be merged.
+ *
+ * Use dedup_variant_t to recursively combine alternative types of all the
+ * supplied variants without duplication.
+ */
+template <typename D, typename... Done, typename... Next, typename... Rest>
+struct merge_variants<std::variant<D, Done...>, std::variant<Next...>,
+                      Rest...> :
+    public merge_variants<
+        sdbusplus::utility::dedup_variant_t<D, Done..., Next...>, Rest...>
+{};
+
+} // namespace details
+
+/** This type is useful for processing D-Bus messages containing information for
+ * multiple interfaces. For doing sdbusplus::message::message::read() on them
+ * one needs to define an std::variant which would be a combination of all the
+ * PropertiesVariant's involved. */
+template <typename... T>
+using merge_variants_t = typename details::merge_variants<T...>::type;
+
+} // namespace utility
+} // namespace sdbusplus
diff --git a/test/utility/type_traits.cpp b/test/utility/type_traits.cpp
index a192bdf..8526f29 100644
--- a/test/utility/type_traits.cpp
+++ b/test/utility/type_traits.cpp
@@ -1,4 +1,5 @@
 #include <sdbusplus/utility/dedup_variant.hpp>
+#include <sdbusplus/utility/merge_variants.hpp>
 #include <sdbusplus/utility/type_traits.hpp>
 
 #include <type_traits>
@@ -93,4 +94,13 @@
               std::variant<uint32_t, uint64_t>,
               sdbusplus::utility::dedup_variant_t<uint32_t, uint64_t, size_t>>);
 
+// Tests for merge_variants.
+static_assert(
+    std::is_same_v<std::variant<size_t>,
+                   sdbusplus::utility::merge_variants_t<std::variant<size_t>>>);
+static_assert(std::is_same_v<std::variant<char, uint32_t, uint64_t>,
+                             sdbusplus::utility::merge_variants_t<
+                                 std::variant<char, uint32_t>,
+                                 std::variant<uint32_t, uint64_t, size_t>>>);
+
 } // namespace