types: Force underlying int conversion for enums

Boost 1.76.0 changed the behavior of numeric to not accept implicitly
converted ints from enum class types. This breaks many of our casts and
would require 2 casts in most cases.

This adds a convenience function to do the underlying type conversions
needed to cast enums to ints and vice versa.

Change-Id: Id653d6a10ef5cab8267c174848940807d693dbf1
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/app/watchdog.cpp b/app/watchdog.cpp
index 03c373e..e92cf81 100644
--- a/app/watchdog.cpp
+++ b/app/watchdog.cpp
@@ -234,8 +234,7 @@
             static_cast<uint8_t>(timeoutAction) & wd_timeout_action_mask);
         wd_service.setExpireAction(ipmiActionToWdAction(ipmi_action));
 
-        const auto ipmiTimerUse =
-            static_cast<IpmiTimerUse>(static_cast<uint8_t>(timerUse));
+        const auto ipmiTimerUse = types::enum_cast<IpmiTimerUse>(timerUse);
         wd_service.setTimerUse(ipmiTimerUseToWdTimerUse(ipmiTimerUse));
 
         wd_service.setExpiredTimerUse(WatchdogService::TimerUse::Reserved);
@@ -415,10 +414,12 @@
 
         lastCallSuccessful = true;
         return ipmi::responseSuccess(
-            static_cast<uint3_t>(wdTimerUseToIpmiTimerUse(wd_prop.timerUse)), 0,
-            wd_prop.enabled, timerNotLogFlags,
-            static_cast<uint3_t>(wdActionToIpmiAction(wd_prop.expireAction)), 0,
-            timerPreTimeoutInterrupt, 0, pretimeout, timerUseExpirationFlags,
+            types::enum_cast<uint3_t>(
+                wdTimerUseToIpmiTimerUse(wd_prop.timerUse)),
+            0, wd_prop.enabled, timerNotLogFlags,
+            types::enum_cast<uint3_t>(
+                wdActionToIpmiAction(wd_prop.expireAction)),
+            0, timerPreTimeoutInterrupt, 0, pretimeout, timerUseExpirationFlags,
             initialCountdown, presentCountdown);
     }
     catch (const InternalFailure& e)
diff --git a/chassishandler.cpp b/chassishandler.cpp
index 551683d..4a3252a 100644
--- a/chassishandler.cpp
+++ b/chassishandler.cpp
@@ -1163,7 +1163,7 @@
     constexpr bool coolingFanFault = false;
     // chassisIdentifySupport set because this command is implemented
     constexpr bool chassisIdentifySupport = true;
-    uint2_t chassisIdentifyState = static_cast<uint2_t>(chassisIDState);
+    uint2_t chassisIdentifyState = types::enum_cast<uint2_t>(chassisIDState);
     constexpr bool diagButtonDisabled = false;
     constexpr bool sleepButtonDisabled = false;
     constexpr bool diagButtonDisableAllow = false;
@@ -1268,7 +1268,8 @@
         {
             auto cause =
                 State::Host::convertRestartCauseFromString(restartCauseStr);
-            return restartCauseToIpmiRestartCause(cause);
+            return types::enum_cast<uint4_t>(
+                restartCauseToIpmiRestartCause(cause));
         }
     }
 
@@ -1706,15 +1707,15 @@
 
     IpmiValue bootOption = ipmiDefault;
 
-    if (static_cast<uint8_t>(bootOptionParameter) ==
-        static_cast<uint8_t>(BootOptionParameter::setInProgress))
+    if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
+        BootOptionParameter::setInProgress)
     {
         response.pack(bootOptionParameter, reserved1, transferStatus);
         return ipmi::responseSuccess(std::move(response));
     }
 
-    if (static_cast<uint8_t>(bootOptionParameter) ==
-        static_cast<uint8_t>(BootOptionParameter::bootInfo))
+    if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
+        BootOptionParameter::bootInfo)
     {
         constexpr uint8_t writeMask = 0;
         constexpr uint8_t bootInfoAck = 0;
@@ -1726,8 +1727,8 @@
      * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
      * This is the only parameter used by petitboot.
      */
-    if (static_cast<uint8_t>(bootOptionParameter) ==
-        static_cast<uint8_t>(BootOptionParameter::bootFlags))
+    if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
+        BootOptionParameter::bootFlags)
     {
         using namespace chassis::internal;
         using namespace chassis::internal::cache;
@@ -1806,8 +1807,8 @@
         if ((bootOptionParameter >= oemParmStart) &&
             (bootOptionParameter <= oemParmEnd))
         {
-            if (static_cast<uint8_t>(bootOptionParameter) ==
-                static_cast<uint8_t>(BootOptionParameter::opalNetworkSettings))
+            if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
+                BootOptionParameter::opalNetworkSettings)
             {
                 response.pack(bootOptionParameter, reserved1);
                 int ret = getHostNetworkData(response);
@@ -1843,8 +1844,8 @@
     using namespace boot_options;
     ipmi::Cc rc;
 
-    if (parameterSelector ==
-        static_cast<uint7_t>(BootOptionParameter::setInProgress))
+    if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
+        BootOptionParameter::setInProgress)
     {
         uint2_t setInProgressFlag;
         uint6_t rsvd;
@@ -1870,8 +1871,8 @@
      * This is the only parameter used by petitboot.
      */
 
-    if (parameterSelector ==
-        static_cast<uint7_t>(BootOptionParameter::bootFlags))
+    if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
+        BootOptionParameter::bootFlags)
     {
         uint5_t rsvd;
         bool validFlag;
@@ -1994,8 +1995,8 @@
             return ipmi::responseUnspecifiedError();
         }
     }
-    else if (parameterSelector ==
-             static_cast<uint7_t>(BootOptionParameter::bootInfo))
+    else if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
+             BootOptionParameter::bootInfo)
     {
         uint8_t writeMak;
         uint5_t bootInitiatorAckData;
@@ -2024,8 +2025,8 @@
         if ((parameterSelector >= static_cast<uint7_t>(oemParmStart)) &&
             (parameterSelector <= static_cast<uint7_t>(oemParmEnd)))
         {
-            if (parameterSelector ==
-                static_cast<uint7_t>(BootOptionParameter::opalNetworkSettings))
+            if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
+                BootOptionParameter::opalNetworkSettings)
             {
                 ipmi::Cc ret = setHostNetworkData(data);
                 if (ret != ipmi::ccSuccess)
diff --git a/include/ipmid/message/types.hpp b/include/ipmid/message/types.hpp
index b79ddba..7a2168e 100644
--- a/include/ipmid/message/types.hpp
+++ b/include/ipmid/message/types.hpp
@@ -92,6 +92,11 @@
 template <size_t Bits>
 constexpr auto getNrBits(const std::bitset<Bits>&) -> Size<Bits>;
 
+template <typename U>
+using underlying_t =
+    typename std::conditional_t<std::is_enum_v<U>, std::underlying_type<U>,
+                                std::enable_if<true, U>>::type;
+
 } // namespace details
 
 /**
@@ -107,4 +112,19 @@
 constexpr auto nrFixedBits =
     decltype(details::getNrBits(std::declval<T>()))::value;
 
+/**
+ * @brief Converts a number or enum class to another
+ * @tparam R - The output type
+ * @tparam T - The input type
+ * @param t - An enum or integer value to cast
+ * @return The value in R form
+ */
+template <typename R, typename T>
+inline R enum_cast(T t)
+{
+    auto tu = static_cast<details::underlying_t<T>>(t);
+    auto ru = static_cast<details::underlying_t<R>>(tu);
+    return static_cast<R>(ru);
+}
+
 } // namespace types
diff --git a/transporthandler.cpp b/transporthandler.cpp
index 00202c5..f1b733e 100644
--- a/transporthandler.cpp
+++ b/transporthandler.cpp
@@ -522,10 +522,10 @@
     }
 
     ret.pack(set);
-    ret.pack(static_cast<uint4_t>(source), uint3_t{}, enabled);
+    ret.pack(types::enum_cast<uint4_t>(source), uint3_t{}, enabled);
     ret.pack(std::string_view(reinterpret_cast<char*>(&addr), sizeof(addr)));
     ret.pack(prefix);
-    ret.pack(static_cast<uint8_t>(status));
+    ret.pack(types::enum_cast<uint8_t>(status));
 }
 
 /** @brief Gets the vlan ID configured on the interface
@@ -1276,7 +1276,7 @@
             {
                 status = SetStatus::Complete;
             }
-            ret.pack(static_cast<uint2_t>(status), uint6_t{});
+            ret.pack(types::enum_cast<uint2_t>(status), uint6_t{});
             return responseSuccess(std::move(ret));
         }
         case LanParam::AuthSupport:
@@ -1316,7 +1316,7 @@
             {
                 src = IPSrc::DHCP;
             }
-            ret.pack(static_cast<uint4_t>(src), uint4_t{});
+            ret.pack(types::enum_cast<uint4_t>(src), uint4_t{});
             return responseSuccess(std::move(ret));
         }
         case LanParam::MAC:
diff --git a/user_channel/channelcommands.cpp b/user_channel/channelcommands.cpp
index b774d3f..de32316 100644
--- a/user_channel/channelcommands.cpp
+++ b/user_channel/channelcommands.cpp
@@ -177,7 +177,8 @@
         return responseInvalidFieldRequest();
     }
 
-    if ((accessSetMode == doNotSet) || (accessSetMode == reserved))
+    if ((types::enum_cast<EChannelActionType>(accessSetMode) == doNotSet) ||
+        (types::enum_cast<EChannelActionType>(accessSetMode) == reserved))
     {
         log<level::DEBUG>("Get channel access - Invalid Access mode");
         return responseInvalidFieldRequest();
@@ -197,11 +198,11 @@
 
     Cc compCode;
 
-    if (accessSetMode == nvData)
+    if (types::enum_cast<EChannelActionType>(accessSetMode) == nvData)
     {
         compCode = getChannelAccessPersistData(chNum, chAccess);
     }
-    else if (accessSetMode == activeData)
+    else if (types::enum_cast<EChannelActionType>(accessSetMode) == activeData)
     {
         compCode = getChannelAccessData(chNum, chAccess);
     }
@@ -215,9 +216,10 @@
     constexpr uint4_t reservedOut2 = 0;
 
     return responseSuccess(
-        static_cast<uint3_t>(chAccess.accessMode), chAccess.userAuthDisabled,
-        chAccess.perMsgAuthDisabled, chAccess.alertingDisabled, reservedOut1,
-        static_cast<uint4_t>(chAccess.privLimit), reservedOut2);
+        types::enum_cast<uint3_t>(chAccess.accessMode),
+        chAccess.userAuthDisabled, chAccess.perMsgAuthDisabled,
+        chAccess.alertingDisabled, reservedOut1,
+        types::enum_cast<uint4_t>(chAccess.privLimit), reservedOut2);
 }
 
 /** @brief implements the get channel info command