message: pack: add variant support

Add variant support to allow return of mutliple specific
types. Also change types to const as this is required by
the visitor and these could have been const all along.

Tested: Added unit test and used in oem provider

Change-Id: I5cb056c15d4813b9eee58eecb707664477d019d9
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/include/ipmid/message/pack.hpp b/include/ipmid/message/pack.hpp
index e6bbbce..104354d 100644
--- a/include/ipmid/message/pack.hpp
+++ b/include/ipmid/message/pack.hpp
@@ -72,7 +72,7 @@
      *  @param[in] p - Payload to pack into.
      *  @param[out] t - The reference to pack item into.
      */
-    static int op(Payload& p, T& t)
+    static int op(Payload& p, const T& t)
     {
         // if not on a byte boundary, must pack values LSbit/LSByte first
         if (p.bitCount)
@@ -96,7 +96,7 @@
 template <>
 struct PackSingle<std::string>
 {
-    static int op(Payload& p, std::string& t)
+    static int op(Payload& p, const std::string& t)
     {
         // check length first
         uint8_t len;
@@ -118,7 +118,7 @@
 template <unsigned N>
 struct PackSingle<fixed_uint_t<N>>
 {
-    static int op(Payload& p, fixed_uint_t<N>& t)
+    static int op(Payload& p, const fixed_uint_t<N>& t)
     {
         size_t count = N;
         static_assert(N <= (details::bitStreamSize - CHAR_BIT));
@@ -138,7 +138,7 @@
 template <>
 struct PackSingle<bool>
 {
-    static int op(Payload& p, bool& b)
+    static int op(Payload& p, const bool& b)
     {
         p.appendBits(1, b);
         return 0;
@@ -149,7 +149,7 @@
 template <size_t N>
 struct PackSingle<std::bitset<N>>
 {
-    static int op(Payload& p, std::bitset<N>& t)
+    static int op(Payload& p, const std::bitset<N>& t)
     {
         size_t count = N;
         static_assert(N <= (details::bitStreamSize - CHAR_BIT));
@@ -169,10 +169,10 @@
 template <typename T, size_t N>
 struct PackSingle<std::array<T, N>>
 {
-    static int op(Payload& p, std::array<T, N>& t)
+    static int op(Payload& p, const std::array<T, N>& t)
     {
         int ret = 0;
-        for (auto& v : t)
+        for (const auto& v : t)
         {
             int ret = PackSingle<T>::op(p, v);
             if (ret)
@@ -188,10 +188,10 @@
 template <typename T>
 struct PackSingle<std::vector<T>>
 {
-    static int op(Payload& p, std::vector<T>& t)
+    static int op(Payload& p, const std::vector<T>& t)
     {
         int ret = 0;
-        for (auto& v : t)
+        for (const auto& v : t)
         {
             int ret = PackSingle<T>::op(p, v);
             if (ret)
@@ -207,7 +207,7 @@
 template <>
 struct PackSingle<std::vector<uint8_t>>
 {
-    static int op(Payload& p, std::vector<uint8_t>& t)
+    static int op(Payload& p, const std::vector<uint8_t>& t)
     {
         p.raw.reserve(p.raw.size() + t.size());
         p.raw.insert(p.raw.end(), t.begin(), t.end());
@@ -215,6 +215,20 @@
     }
 };
 
+/** @brief Specialization of PackSingle for std::variant<T, N> */
+template <typename... T>
+struct PackSingle<std::variant<T...>>
+{
+    static int op(Payload& p, const std::variant<T...>& v)
+    {
+        return std::visit(
+            [&p](const auto& arg) {
+                return PackSingle<std::decay_t<decltype(arg)>>::op(p, arg);
+            },
+            v);
+    }
+};
+
 } // namespace details
 
 } // namespace message
diff --git a/test/message/pack.cpp b/test/message/pack.cpp
index 60459ed..b3957cc 100644
--- a/test/message/pack.cpp
+++ b/test/message/pack.cpp
@@ -265,6 +265,21 @@
     ASSERT_EQ(p.raw, k);
 }
 
+TEST(PackAdvanced, VariantArray)
+{
+    ipmi::message::Payload p;
+    std::variant<std::array<uint8_t, 2>, uint32_t> variant;
+    auto data = std::array<uint8_t, 2>{2, 4};
+    variant = data;
+
+    p.pack(variant);
+    ASSERT_EQ(p.size(), sizeof(data));
+
+    // check that the bytes were correctly packed packed (LSB first)
+    std::vector<uint8_t> k = {2, 4};
+    ASSERT_EQ(p.raw, k);
+}
+
 TEST(PackAdvanced, BoolsnBitfieldsnFixedIntsOhMy)
 {
     // each element will be added, filling the low-order bits first