util: backport std::to_underlying()

Change-Id: I239ceb6be20d56343917cfcd7e1049fc852c1ced
diff --git a/ndn-cxx/lp/cache-policy.cpp b/ndn-cxx/lp/cache-policy.cpp
index 1bc1aa1..b4f4d82 100644
--- a/ndn-cxx/lp/cache-policy.cpp
+++ b/ndn-cxx/lp/cache-policy.cpp
@@ -32,14 +32,10 @@
 {
   switch (policy) {
   case CachePolicyType::NO_CACHE:
-    os << "NoCache";
-    break;
+    return os << "NoCache";
   default:
-    os << "None";
-    break;
+    return os << "None";
   }
-
-  return os;
 }
 
 CachePolicy::CachePolicy()
diff --git a/ndn-cxx/lp/nack-header.cpp b/ndn-cxx/lp/nack-header.cpp
index b9f5ffb..b3053be 100644
--- a/ndn-cxx/lp/nack-header.cpp
+++ b/ndn-cxx/lp/nack-header.cpp
@@ -31,19 +31,14 @@
 {
   switch (reason) {
   case NackReason::CONGESTION:
-    os << "Congestion";
-    break;
+    return os << "Congestion";
   case NackReason::DUPLICATE:
-    os << "Duplicate";
-    break;
+    return os << "Duplicate";
   case NackReason::NO_ROUTE:
-    os << "NoRoute";
-    break;
+    return os << "NoRoute";
   default:
-    os << "None";
-    break;
+    return os << "None";
   }
-  return os;
 }
 
 bool
@@ -56,7 +51,7 @@
     return true;
   }
 
-  return static_cast<int>(x) < static_cast<int>(y);
+  return to_underlying(x) < to_underlying(y);
 }
 
 NackHeader::NackHeader()
diff --git a/ndn-cxx/name-component.cpp b/ndn-cxx/name-component.cpp
index b19eacc..b7b9333 100644
--- a/ndn-cxx/name-component.cpp
+++ b/ndn-cxx/name-component.cpp
@@ -77,13 +77,13 @@
 static bool
 canDecodeMarkerConvention()
 {
-  return (static_cast<int>(g_conventionDecoding) & static_cast<int>(Convention::MARKER)) != 0;
+  return (to_underlying(g_conventionDecoding) & to_underlying(Convention::MARKER)) != 0;
 }
 
 static bool
 canDecodeTypedConvention()
 {
-  return (static_cast<int>(g_conventionDecoding) & static_cast<int>(Convention::TYPED)) != 0;
+  return (to_underlying(g_conventionDecoding) & to_underlying(Convention::TYPED)) != 0;
 }
 
 void
diff --git a/ndn-cxx/security/security-common.cpp b/ndn-cxx/security/security-common.cpp
index 4de326d..abf0b32 100644
--- a/ndn-cxx/security/security-common.cpp
+++ b/ndn-cxx/security/security-common.cpp
@@ -36,7 +36,7 @@
     case KeyIdType::RANDOM:
       return os << "RANDOM";
   }
-  return os << static_cast<int>(keyIdType);
+  return os << to_underlying(keyIdType);
 }
 
 std::ostream&
@@ -54,7 +54,7 @@
     case KeyType::HMAC:
       return os << "HMAC";
   }
-  return os << static_cast<int>(keyType);
+  return os << to_underlying(keyType);
 }
 
 std::ostream&
@@ -84,7 +84,7 @@
     case DigestAlgorithm::SHA3_512:
       return os << "SHA3-512";
   }
-  return os << static_cast<int>(algorithm);
+  return os << to_underlying(algorithm);
 }
 
 std::ostream&
@@ -96,7 +96,7 @@
     case BlockCipherAlgorithm::AES_CBC:
       return os << "AES-CBC";
   }
-  return os << static_cast<int>(algorithm);
+  return os << to_underlying(algorithm);
 }
 
 std::ostream&
@@ -108,7 +108,7 @@
     case CipherOperator::ENCRYPT:
       return os << "ENCRYPT";
   }
-  return os << static_cast<int>(op);
+  return os << to_underlying(op);
 }
 
 } // namespace ndn
diff --git a/ndn-cxx/security/v2/validation-error.cpp b/ndn-cxx/security/v2/validation-error.cpp
index 309d0c1..6df5f23 100644
--- a/ndn-cxx/security/v2/validation-error.cpp
+++ b/ndn-cxx/security/v2/validation-error.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2018 Regents of the University of California.
+ * Copyright (c) 2013-2019 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -57,10 +57,10 @@
       break;
   }
   if (code >= ValidationError::Code::USER_MIN) {
-    return os << "Custom error code " << static_cast<uint32_t>(code);
+    return os << "Custom error code " << to_underlying(code);
   }
   else {
-    return os << "Unrecognized error code " << static_cast<uint32_t>(code);
+    return os << "Unrecognized error code " << to_underlying(code);
   }
 }
 
diff --git a/ndn-cxx/util/backports.hpp b/ndn-cxx/util/backports.hpp
index afe0036..e87f0e0 100644
--- a/ndn-cxx/util/backports.hpp
+++ b/ndn-cxx/util/backports.hpp
@@ -40,6 +40,10 @@
 #  define NDN_CXX_HAS_INCLUDE(x) 0
 #endif
 
+//
+// http://wg21.link/P0188
+// [[fallthrough]] attribute (C++17)
+//
 #if (__cplusplus > 201402L) && NDN_CXX_HAS_CPP_ATTRIBUTE(fallthrough)
 #  define NDN_CXX_FALLTHROUGH [[fallthrough]]
 #elif NDN_CXX_HAS_CPP_ATTRIBUTE(clang::fallthrough)
@@ -74,24 +78,33 @@
 
 namespace ndn {
 
+//
+// https://redmine.named-data.net/issues/2743
+// std::to_string() (C++11)
+//
 #ifdef NDN_CXX_HAVE_STD_TO_STRING
 using std::to_string;
 #else
-template<typename V>
+template<typename T>
 inline std::string
-to_string(const V& v)
+to_string(const T& val)
 {
-  return boost::lexical_cast<std::string>(v);
+  return boost::lexical_cast<std::string>(val);
 }
 #endif // NDN_CXX_HAVE_STD_TO_STRING
 
-#if __cpp_lib_clamp >= 201603
+//
+// https://wg21.link/P0025
+// std::clamp() (C++17)
+//
+#if __cpp_lib_clamp >= 201603L
 using std::clamp;
 #else
 template<typename T, typename Compare>
 constexpr const T&
 clamp(const T& v, const T& lo, const T& hi, Compare comp)
 {
+  BOOST_ASSERT(!comp(hi, lo));
   return comp(v, lo) ? lo : comp(hi, v) ? hi : v;
 }
 
@@ -99,10 +112,27 @@
 constexpr const T&
 clamp(const T& v, const T& lo, const T& hi)
 {
+  BOOST_ASSERT(!(hi < lo));
   return (v < lo) ? lo : (hi < v) ? hi : v;
 }
 #endif // __cpp_lib_clamp
 
+//
+// https://wg21.link/P1682
+// std::to_underlying() (approved for LWG as of July 2019)
+//
+#if __cpp_lib_to_underlying >= 202002L
+using std::to_underlying;
+#else
+template<typename T>
+constexpr std::underlying_type_t<T>
+to_underlying(T val) noexcept
+{
+  static_assert(std::is_enum<T>::value, "");
+  return static_cast<std::underlying_type_t<T>>(val);
+}
+#endif // __cpp_lib_to_underlying
+
 using ::nonstd::any;
 using ::nonstd::any_cast;
 using ::nonstd::bad_any_cast;
diff --git a/ndn-cxx/util/logger.cpp b/ndn-cxx/util/logger.cpp
index 15203ec..f845ff8 100644
--- a/ndn-cxx/util/logger.cpp
+++ b/ndn-cxx/util/logger.cpp
@@ -49,7 +49,7 @@
     return os << "ALL";
   }
 
-  NDN_THROW(std::invalid_argument("unknown log level " + to_string(static_cast<int>(level))));
+  NDN_THROW(std::invalid_argument("unknown log level " + to_string(to_underlying(level))));
 }
 
 LogLevel
diff --git a/tests/unit/data.t.cpp b/tests/unit/data.t.cpp
index 08a5cef..9457089 100644
--- a/tests/unit/data.t.cpp
+++ b/tests/unit/data.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2018 Regents of the University of California.
+ * Copyright (c) 2013-2019 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -211,7 +211,7 @@
   Data d(dataBlock);
 
   BOOST_CHECK_EQUAL(d.getName().toUri(), "/local/ndn/prefix");
-  BOOST_CHECK_EQUAL(d.getContentType(), static_cast<uint32_t>(tlv::ContentType_Blob));
+  BOOST_CHECK_EQUAL(d.getContentType(), tlv::ContentType_Blob);
   BOOST_CHECK_EQUAL(d.getFreshnessPeriod(), 10_s);
   BOOST_CHECK_EQUAL(std::string(reinterpret_cast<const char*>(d.getContent().value()),
                                 d.getContent().value_size()), "SUCCESS!");