Emulate std::to_string on platforms that do not provide it

Inspired by NFD commit ce81230b09583f9aec98a97653a047aa54fa2bef

This commit also fixes an important typo in the name of the macro
defined by the check_vector_const_iterators configure check.

Change-Id: I1b18066474145720570b5ecd6109b3f76eb262e8
Refs: #2743
diff --git a/.waf-tools/compiler-features.py b/.waf-tools/compiler-features.py
index 098e51f..8389025 100644
--- a/.waf-tools/compiler-features.py
+++ b/.waf-tools/compiler-features.py
@@ -70,6 +70,32 @@
                       features='cxx', mandatory=False):
         self.define('HAVE_CXX_OVERRIDE_FINAL', 1)
 
+STD_TO_STRING = '''
+#include <string>
+int
+main(int argc, char** argv)
+{
+  std::string s = std::to_string(0);
+  s = std::to_string(0l);
+  s = std::to_string(0ll);
+  s = std::to_string(0u);
+  s = std::to_string(0ul);
+  s = std::to_string(0ull);
+  s = std::to_string(0.0f);
+  s = std::to_string(0.0);
+  s = std::to_string(0.0l);
+  s.clear();
+  return 0;
+}
+'''
+
+@conf
+def check_std_to_string(self):
+    if self.check_cxx(msg='Checking for std::to_string',
+                      fragment=STD_TO_STRING,
+                      features='cxx', mandatory=False):
+        self.define('HAVE_STD_TO_STRING', 1)
+
 VECTOR_INSERT_ERASE_CONST_ITERATOR = '''
 #include <vector>
 int
@@ -87,12 +113,13 @@
 
 @conf
 def check_vector_const_iterators(self):
-    if self.check_cxx(msg='Checking for std::vector::insert with const_iterators',
+    if self.check_cxx(msg='Checking for std::vector::insert with const_iterator',
                       fragment=VECTOR_INSERT_ERASE_CONST_ITERATOR,
                       features='cxx', mandatory=False):
-        self.define('NDN_CXX_HAVE_VECTOR_INSERT_ERASE_CONST_ITERATOR', 1)
+        self.define('HAVE_VECTOR_INSERT_ERASE_CONST_ITERATOR', 1)
 
 def configure(conf):
     conf.check_friend_typename()
     conf.check_override()
+    conf.check_std_to_string()
     conf.check_vector_const_iterators()
diff --git a/src/lp/detail/field-decl.hpp b/src/lp/detail/field-decl.hpp
index 0738768..6804b8a 100644
--- a/src/lp/detail/field-decl.hpp
+++ b/src/lp/detail/field-decl.hpp
@@ -44,7 +44,7 @@
   decode(const Block& wire)
   {
     if (wire.type() != TlvType::value) {
-      BOOST_THROW_EXCEPTION(ndn::tlv::Error("Unexpected TLV type " + std::to_string(wire.type())));
+      BOOST_THROW_EXCEPTION(ndn::tlv::Error("Unexpected TLV type " + to_string(wire.type())));
     }
 
     T type;
@@ -60,7 +60,7 @@
   decode(const Block& wire)
   {
     if (wire.type() != TlvType::value) {
-      BOOST_THROW_EXCEPTION(ndn::tlv::Error("Unexpected TLV type " + std::to_string(wire.type())));
+      BOOST_THROW_EXCEPTION(ndn::tlv::Error("Unexpected TLV type " + to_string(wire.type())));
     }
 
     return readNonNegativeInteger(wire);
@@ -74,11 +74,11 @@
   decode(const Block& wire)
   {
     if (wire.type() != TlvType::value) {
-      BOOST_THROW_EXCEPTION(ndn::tlv::Error("Unexpected TLV type " + std::to_string(wire.type())));
+      BOOST_THROW_EXCEPTION(ndn::tlv::Error("Unexpected TLV type " + to_string(wire.type())));
     }
 
     if (wire.value_size() == 0) {
-      BOOST_THROW_EXCEPTION(ndn::tlv::Error(std::to_string(wire.type()) + " must not be empty"));
+      BOOST_THROW_EXCEPTION(ndn::tlv::Error(to_string(wire.type()) + " must not be empty"));
     }
 
     return std::make_pair(wire.value_begin(), wire.value_end());
diff --git a/src/name-component.cpp b/src/name-component.cpp
index 9226eb4..ac0f5f1 100644
--- a/src/name-component.cpp
+++ b/src/name-component.cpp
@@ -366,7 +366,7 @@
 {
   if (digest->size() != crypto::SHA256_DIGEST_SIZE)
     BOOST_THROW_EXCEPTION(Error("Cannot create ImplicitSha256DigestComponent (input digest must be " +
-                                std::to_string(crypto::SHA256_DIGEST_SIZE) + " octets)"));
+                                to_string(crypto::SHA256_DIGEST_SIZE) + " octets)"));
 
   return Block(tlv::ImplicitSha256DigestComponent, digest);
 }
@@ -375,8 +375,8 @@
 Component::fromImplicitSha256Digest(const uint8_t* digest, size_t digestSize)
 {
   if (digestSize != crypto::SHA256_DIGEST_SIZE)
-    BOOST_THROW_EXCEPTION(Error("Cannot create ImplicitSha256DigestComponent (input digest must be "
-                                + std::to_string(crypto::SHA256_DIGEST_SIZE) + " octets)"));
+    BOOST_THROW_EXCEPTION(Error("Cannot create ImplicitSha256DigestComponent (input digest must be " +
+                                to_string(crypto::SHA256_DIGEST_SIZE) + " octets)"));
 
   return makeBinaryBlock(tlv::ImplicitSha256DigestComponent, digest, digestSize);
 }
diff --git a/src/util/backports.hpp b/src/util/backports.hpp
index 19b33c4..42597e9 100644
--- a/src/util/backports.hpp
+++ b/src/util/backports.hpp
@@ -30,6 +30,10 @@
 
 #include "../common.hpp"
 
+#ifndef NDN_CXX_HAVE_STD_TO_STRING
+#include <boost/lexical_cast.hpp>
+#endif
+
 namespace ndn {
 
 #if __cpp_lib_make_unique
@@ -43,6 +47,17 @@
 }
 #endif // __cpp_lib_make_unique
 
+#ifdef NDN_CXX_HAVE_STD_TO_STRING
+using std::to_string;
+#else
+template<typename V>
+inline std::string
+to_string(const V& v)
+{
+  return boost::lexical_cast<std::string>(v);
+}
+#endif // NDN_CXX_HAVE_STD_TO_STRING
+
 } // namespace ndn
 
 #endif // NDN_UTIL_BACKPORTS_HPP