folly: to: make exceptions more informative
authorLucian Grijincu <lucian@fb.com>
Mon, 26 Jan 2015 21:43:36 +0000 (13:43 -0800)
committerwoo <woo@fb.com>
Mon, 2 Feb 2015 21:12:44 +0000 (13:12 -0800)
Summary: print the value which failed conversion to aid debugging.

Test Plan: run code which throws in to<> and contbuild

Reviewed By: tudorb@fb.com, andrei.alexandrescu@fb.com

Subscribers: trunkagent, folly-diffs@

FB internal diff: D1786294

Signature: t1:1786294:1421795912:00cc10923990e5aac0a15eedec09deb8e8d470d1

folly/Conv.cpp
folly/Conv.h

index 25c0069dd0bdbd5c2a673e441b072303ce766d4d..e5de9a2f9c173cca2d3cc31f8ebeaedc30258803 100644 (file)
@@ -71,8 +71,8 @@ inline bool bool_str_cmp(const char** b, size_t len, const char* value) {
 bool str_to_bool(StringPiece* src) {
   auto b = src->begin(), e = src->end();
   for (;; ++b) {
-    FOLLY_RANGE_CHECK(b < e,
-                      "No non-whitespace characters found in input string");
+    FOLLY_RANGE_CHECK_STRINGPIECE(
+      b < e, "No non-whitespace characters found in input string", *src);
     if (!isspace(*b)) break;
   }
 
@@ -85,8 +85,8 @@ bool str_to_bool(StringPiece* src) {
       StringPiece tmp(*src);
       uint8_t value = to<uint8_t>(&tmp);
       // Only accept 0 or 1
-      FOLLY_RANGE_CHECK(value <= 1,
-                        "Integer overflow when parsing bool: must be 0 or 1");
+      FOLLY_RANGE_CHECK_STRINGPIECE(
+        value <= 1, "Integer overflow when parsing bool: must be 0 or 1", *src);
       b = tmp.begin();
       result = (value == 1);
       break;
@@ -126,11 +126,11 @@ bool str_to_bool(StringPiece* src) {
       } else if (bool_str_cmp(&b, len, "off")) {
         result = false;
       } else {
-        FOLLY_RANGE_CHECK(false, "Invalid value for bool");
+        FOLLY_RANGE_CHECK_STRINGPIECE(false, "Invalid value for bool", *src);
       }
       break;
     default:
-      FOLLY_RANGE_CHECK(false, "Invalid value for bool");
+      FOLLY_RANGE_CHECK_STRINGPIECE(false, "Invalid value for bool", *src);
   }
 
   src->assign(b, e);
index 5a196463dd79df1ee6fbfd73375c65be3a23a39e..31902c73d8008ed4dced18c224aee4c2e45f7e20 100644 (file)
 #define FOLLY_RANGE_CHECK_STRINGIZE(x) #x
 #define FOLLY_RANGE_CHECK_STRINGIZE2(x) FOLLY_RANGE_CHECK_STRINGIZE(x)
 
-#define FOLLY_RANGE_CHECK(condition, message)                               \
+#define FOLLY_RANGE_CHECK(condition, message, src)                          \
   ((condition) ? (void)0 : throw std::range_error(                          \
     (std::string(__FILE__ "(" FOLLY_RANGE_CHECK_STRINGIZE2(__LINE__) "): ") \
-     + (message)).c_str()))
+     + (message) + ": '" + (src) + "'").c_str()))
+
+#define FOLLY_RANGE_CHECK_BEGIN_END(condition, message, b, e)    \
+  FOLLY_RANGE_CHECK(condition, message, std::string((b), (e) - (b)))
+
+#define FOLLY_RANGE_CHECK_STRINGPIECE(condition, message, sp)    \
+  FOLLY_RANGE_CHECK(condition, message, std::string((sp).data(), (sp).size()))
 
 namespace folly {
 
@@ -89,14 +95,14 @@ to(const Src & value) {
                    < std::numeric_limits<Src>::max()) {
     FOLLY_RANGE_CHECK(
       (!greater_than<Tgt, std::numeric_limits<Tgt>::max()>(value)),
-      "Overflow"
+      "Overflow", std::to_string(value)
     );
   }
   /* static */ if (std::is_signed<Src>::value &&
                    (!std::is_signed<Tgt>::value || sizeof(Src) > sizeof(Tgt))) {
     FOLLY_RANGE_CHECK(
       (!less_than<Tgt, std::numeric_limits<Tgt>::min()>(value)),
-      "Negative overflow"
+      "Negative overflow", std::to_string(value)
     );
   }
   return static_cast<Tgt>(value);
@@ -116,9 +122,9 @@ to(const Src & value) {
   /* static */ if (std::numeric_limits<Tgt>::max() <
                    std::numeric_limits<Src>::max()) {
     FOLLY_RANGE_CHECK(value <= std::numeric_limits<Tgt>::max(),
-                      "Overflow");
+                      "Overflow", std::to_string(value));
     FOLLY_RANGE_CHECK(value >= -std::numeric_limits<Tgt>::max(),
-                      "Negative overflow");
+                      "Negative overflow", std::to_string(value));
   }
   return boost::implicit_cast<Tgt>(value);
 }
@@ -1075,9 +1081,10 @@ __attribute__((__aligned__(16))) constexpr uint16_t shift1000[] = {
           if (*b != '0') return digits_to<Tgt>(b, e);
         }
       }
-      FOLLY_RANGE_CHECK(size == std::numeric_limits<Tgt>::digits10 + 1 &&
-                        strncmp(b, detail::MaxString<Tgt>::value, size) <= 0,
-                        "Numeric overflow upon conversion");
+      FOLLY_RANGE_CHECK_BEGIN_END(
+        size == std::numeric_limits<Tgt>::digits10 + 1 &&
+        strncmp(b, detail::MaxString<Tgt>::value, size) <= 0,
+        "Numeric overflow upon conversion", b, e);
     }
 
     // Here we know that the number won't overflow when
@@ -1120,7 +1127,8 @@ __attribute__((__aligned__(16))) constexpr uint16_t shift1000[] = {
     }
 
     assert(b == e);
-    FOLLY_RANGE_CHECK(size > 0, "Found no digits to convert in input");
+    FOLLY_RANGE_CHECK_BEGIN_END(size > 0,
+                                "Found no digits to convert in input", b, e);
     return result;
   }
 
@@ -1152,18 +1160,19 @@ typename std::enable_if<
   std::is_integral<Tgt>::value && std::is_signed<Tgt>::value,
   Tgt>::type
 to(const char * b, const char * e) {
-  FOLLY_RANGE_CHECK(b < e, "Empty input string in conversion to integral");
+  FOLLY_RANGE_CHECK(b < e, "Empty input string in conversion to integral",
+                    to<std::string>("b: ", intptr_t(b), " e: ", intptr_t(e)));
   if (!isdigit(*b)) {
     if (*b == '-') {
       Tgt result = -to<typename std::make_unsigned<Tgt>::type>(b + 1, e);
-      FOLLY_RANGE_CHECK(result <= 0, "Negative overflow.");
+      FOLLY_RANGE_CHECK_BEGIN_END(result <= 0, "Negative overflow.", b, e);
       return result;
     }
-    FOLLY_RANGE_CHECK(*b == '+', "Invalid lead character");
+    FOLLY_RANGE_CHECK_BEGIN_END(*b == '+', "Invalid lead character", b, e);
     ++b;
   }
   Tgt result = to<typename std::make_unsigned<Tgt>::type>(b, e);
-  FOLLY_RANGE_CHECK(result >= 0, "Overflow.");
+  FOLLY_RANGE_CHECK_BEGIN_END(result >= 0, "Overflow", b, e);
   return result;
 }
 
@@ -1186,7 +1195,8 @@ to(StringPiece * src) {
 
   auto b = src->data(), past = src->data() + src->size();
   for (;; ++b) {
-    FOLLY_RANGE_CHECK(b < past, "No digits found in input string");
+    FOLLY_RANGE_CHECK_STRINGPIECE(b < past,
+                                  "No digits found in input string", *src);
     if (!isspace(*b)) break;
   }
 
@@ -1199,15 +1209,16 @@ to(StringPiece * src) {
       if (*m == '-') {
         negative = true;
       } else {
-        FOLLY_RANGE_CHECK(*m == '+', "Invalid leading character in conversion"
-                          " to integral");
+        FOLLY_RANGE_CHECK_STRINGPIECE(*m == '+', "Invalid leading character in "
+                                      "conversion to integral", *src);
       }
       ++b;
       ++m;
     }
   }
-  FOLLY_RANGE_CHECK(m < past, "No digits found in input string");
-  FOLLY_RANGE_CHECK(isdigit(*m), "Non-digit character found");
+  FOLLY_RANGE_CHECK_STRINGPIECE(m < past, "No digits found in input string",
+                                *src);
+  FOLLY_RANGE_CHECK_STRINGPIECE(isdigit(*m), "Non-digit character found", *src);
   m = detail::findFirstNonDigit<Tgt>(m + 1, past);
 
   Tgt result;
@@ -1217,10 +1228,11 @@ to(StringPiece * src) {
     auto t = detail::digits_to<typename std::make_unsigned<Tgt>::type>(b, m);
     if (negative) {
       result = -t;
-      FOLLY_RANGE_CHECK(is_non_positive(result), "Negative overflow");
+      FOLLY_RANGE_CHECK_STRINGPIECE(is_non_positive(result),
+                                    "Negative overflow", *src);
     } else {
       result = t;
-      FOLLY_RANGE_CHECK(is_non_negative(result), "Overflow");
+      FOLLY_RANGE_CHECK_STRINGPIECE(is_non_negative(result), "Overflow", *src);
     }
   }
   src->advance(m - src->data());
@@ -1246,7 +1258,9 @@ namespace detail {
  */
 inline void enforceWhitespace(const char* b, const char* e) {
   for (; b != e; ++b) {
-    FOLLY_RANGE_CHECK(isspace(*b), to<std::string>("Non-whitespace: ", *b));
+    FOLLY_RANGE_CHECK_BEGIN_END(isspace(*b),
+                                to<std::string>("Non-whitespace: ", *b),
+                                b, e);
   }
 }
 
@@ -1288,7 +1302,8 @@ to(StringPiece *const src) {
          std::numeric_limits<double>::quiet_NaN(),
          nullptr, nullptr);
 
-  FOLLY_RANGE_CHECK(!src->empty(), "No digits found in input string");
+  FOLLY_RANGE_CHECK_STRINGPIECE(!src->empty(),
+                                "No digits found in input string", *src);
 
   int length;
   auto result = conv.StringToDouble(src->data(),
@@ -1476,8 +1491,10 @@ to(const Src & value) {
 // to avoid defining this global macro name in other files that include Conv.h.
 #ifndef FOLLY_CONV_INTERNAL
 #undef FOLLY_RANGE_CHECK
-#undef FOLLY_RANGE_CHECK_STRINGIZE2
+#undef FOLLY_RANGE_CHECK_BEGIN_END
+#undef FOLLY_RANGE_CHECK_STRINGPIECE
 #undef FOLLY_RANGE_CHECK_STRINGIZE
+#undef FOLLY_RANGE_CHECK_STRINGIZE2
 #endif
 
 #endif /* FOLLY_BASE_CONV_H_ */