util: Refactor IP toString

Change-Id: I9aea159e95e5b66d6a02809c3d5832c8697df2e1
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/util.cpp b/src/util.cpp
index 915f2b4..7ede99c 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -164,44 +164,23 @@
         [=]<int f>() -> InAddrAny { return addrFromBuf<f>(buf); }, family);
 }
 
-std::string toString(const struct in_addr& addr)
+template <typename Addr>
+std::string toString(const Addr& addr)
 {
-    std::string ip(INET_ADDRSTRLEN, '\0');
-    if (inet_ntop(AF_INET, &addr, ip.data(), ip.size()) == nullptr)
+    static constexpr int family = AddrToFamily<Addr>::value;
+    std::string ret(FamilyTraits<family>::strlen, '\0');
+    if (inet_ntop(family, &addr, ret.data(), ret.size()) == nullptr)
     {
-        throw std::runtime_error("Failed to convert IP4 to string");
+        throw std::runtime_error("Failed to convert IP to string");
     }
 
-    ip.resize(strlen(ip.c_str()));
-    return ip;
-}
-
-std::string toString(const struct in6_addr& addr)
-{
-    std::string ip(INET6_ADDRSTRLEN, '\0');
-    if (inet_ntop(AF_INET6, &addr, ip.data(), ip.size()) == nullptr)
-    {
-        throw std::runtime_error("Failed to convert IP6 to string");
-    }
-
-    ip.resize(strlen(ip.c_str()));
-    return ip;
+    ret.resize(strlen(ret.c_str()));
+    return ret;
 }
 
 std::string toString(const InAddrAny& addr)
 {
-    if (std::holds_alternative<struct in_addr>(addr))
-    {
-        const auto& v = std::get<struct in_addr>(addr);
-        return toString(v);
-    }
-    else if (std::holds_alternative<struct in6_addr>(addr))
-    {
-        const auto& v = std::get<struct in6_addr>(addr);
-        return toString(v);
-    }
-
-    throw std::runtime_error("Invalid addr type");
+    return std::visit([](auto&& a) { return toString(a); }, addr);
 }
 
 bool isValidIP(int family, stdplus::const_zstring address) noexcept
diff --git a/src/util.hpp b/src/util.hpp
index 84c6574..52abfb7 100644
--- a/src/util.hpp
+++ b/src/util.hpp
@@ -81,12 +81,31 @@
 struct FamilyTraits<AF_INET>
 {
     using addr = in_addr;
+    static constexpr std::size_t strlen = INET_ADDRSTRLEN;
 };
 
 template <>
 struct FamilyTraits<AF_INET6>
 {
     using addr = in6_addr;
+    static constexpr std::size_t strlen = INET6_ADDRSTRLEN;
+};
+
+template <typename Addr>
+struct AddrToFamily
+{
+};
+
+template <>
+struct AddrToFamily<in_addr>
+{
+    static constexpr int value = AF_INET;
+};
+
+template <>
+struct AddrToFamily<in6_addr>
+{
+    static constexpr int value = AF_INET6;
 };
 
 /* @brief converts a sockaddr for the specified address family into
@@ -102,9 +121,9 @@
  * @param[in] addr - input ip address to convert.
  * @returns String representation of the ip.
  */
+template <typename Addr>
+std::string toString(const Addr& addr);
 std::string toString(const InAddrAny& addr);
-std::string toString(const struct in_addr& addr);
-std::string toString(const struct in6_addr& addr);
 
 /* @brief checks that the given ip address valid or not.
  * @param[in] family - IP address family(AF_INET/AF_INET6).