Rework RE2::Arg with templates instead of macros.

Bump SONAME accordingly.

Change-Id: Ic62e8de87dda68951c2a49715a826c8f42c2dbe3
Reviewed-on: https://code-review.googlesource.com/c/re2/+/57850
Reviewed-by: Paul Wankadia <junyer@google.com>
diff --git a/Makefile b/Makefile
index fe766d2..18f566f 100644
--- a/Makefile
+++ b/Makefile
@@ -44,7 +44,7 @@
 
 # ABI version
 # http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html
-SONAME=8
+SONAME=9
 
 # To rebuild the Tables generated by Perl and Python scripts (requires Internet
 # access for Unicode data), uncomment the following line:
diff --git a/re2/re2.cc b/re2/re2.cc
index 7ec193c..2a6381f 100644
--- a/re2/re2.cc
+++ b/re2/re2.cc
@@ -1040,41 +1040,49 @@
 
 /***** Parsers for various types *****/
 
-bool RE2::Arg::parse_null(const char* str, size_t n, void* dest) {
+namespace internal {
+
+template <>
+bool Parse(const char* str, size_t n, void* dest) {
   // We fail if somebody asked us to store into a non-NULL void* pointer
   return (dest == NULL);
 }
 
-bool RE2::Arg::parse_string(const char* str, size_t n, void* dest) {
+template <>
+bool Parse(const char* str, size_t n, std::string* dest) {
   if (dest == NULL) return true;
-  reinterpret_cast<std::string*>(dest)->assign(str, n);
+  dest->assign(str, n);
   return true;
 }
 
-bool RE2::Arg::parse_stringpiece(const char* str, size_t n, void* dest) {
+template <>
+bool Parse(const char* str, size_t n, StringPiece* dest) {
   if (dest == NULL) return true;
-  *(reinterpret_cast<StringPiece*>(dest)) = StringPiece(str, n);
+  *dest = StringPiece(str, n);
   return true;
 }
 
-bool RE2::Arg::parse_char(const char* str, size_t n, void* dest) {
+template <>
+bool Parse(const char* str, size_t n, char* dest) {
   if (n != 1) return false;
   if (dest == NULL) return true;
-  *(reinterpret_cast<char*>(dest)) = str[0];
+  *dest = str[0];
   return true;
 }
 
-bool RE2::Arg::parse_schar(const char* str, size_t n, void* dest) {
+template <>
+bool Parse(const char* str, size_t n, signed char* dest) {
   if (n != 1) return false;
   if (dest == NULL) return true;
-  *(reinterpret_cast<signed char*>(dest)) = str[0];
+  *dest = str[0];
   return true;
 }
 
-bool RE2::Arg::parse_uchar(const char* str, size_t n, void* dest) {
+template <>
+bool Parse(const char* str, size_t n, unsigned char* dest) {
   if (n != 1) return false;
   if (dest == NULL) return true;
-  *(reinterpret_cast<unsigned char*>(dest)) = str[0];
+  *dest = str[0];
   return true;
 }
 
@@ -1138,10 +1146,40 @@
   return buf;
 }
 
-bool RE2::Arg::parse_long_radix(const char* str,
-                                size_t n,
-                                void* dest,
-                                int radix) {
+template <>
+bool Parse(const char* str, size_t n, float* dest) {
+  if (n == 0) return false;
+  static const int kMaxLength = 200;
+  char buf[kMaxLength+1];
+  str = TerminateNumber(buf, sizeof buf, str, &n, true);
+  char* end;
+  errno = 0;
+  float r = strtof(str, &end);
+  if (end != str + n) return false;   // Leftover junk
+  if (errno) return false;
+  if (dest == NULL) return true;
+  *dest = r;
+  return true;
+}
+
+template <>
+bool Parse(const char* str, size_t n, double* dest) {
+  if (n == 0) return false;
+  static const int kMaxLength = 200;
+  char buf[kMaxLength+1];
+  str = TerminateNumber(buf, sizeof buf, str, &n, true);
+  char* end;
+  errno = 0;
+  double r = strtod(str, &end);
+  if (end != str + n) return false;   // Leftover junk
+  if (errno) return false;
+  if (dest == NULL) return true;
+  *dest = r;
+  return true;
+}
+
+template <>
+bool Parse(const char* str, size_t n, long* dest, int radix) {
   if (n == 0) return false;
   char buf[kMaxNumberLength+1];
   str = TerminateNumber(buf, sizeof buf, str, &n, false);
@@ -1151,14 +1189,12 @@
   if (end != str + n) return false;   // Leftover junk
   if (errno) return false;
   if (dest == NULL) return true;
-  *(reinterpret_cast<long*>(dest)) = r;
+  *dest = r;
   return true;
 }
 
-bool RE2::Arg::parse_ulong_radix(const char* str,
-                                 size_t n,
-                                 void* dest,
-                                 int radix) {
+template <>
+bool Parse(const char* str, size_t n, unsigned long* dest, int radix) {
   if (n == 0) return false;
   char buf[kMaxNumberLength+1];
   str = TerminateNumber(buf, sizeof buf, str, &n, false);
@@ -1174,62 +1210,52 @@
   if (end != str + n) return false;   // Leftover junk
   if (errno) return false;
   if (dest == NULL) return true;
-  *(reinterpret_cast<unsigned long*>(dest)) = r;
+  *dest = r;
   return true;
 }
 
-bool RE2::Arg::parse_short_radix(const char* str,
-                                 size_t n,
-                                 void* dest,
-                                 int radix) {
+template <>
+bool Parse(const char* str, size_t n, short* dest, int radix) {
   long r;
-  if (!parse_long_radix(str, n, &r, radix)) return false;  // Could not parse
-  if ((short)r != r) return false;                         // Out of range
+  if (!Parse(str, n, &r, radix)) return false;  // Could not parse
+  if ((short)r != r) return false;              // Out of range
   if (dest == NULL) return true;
-  *(reinterpret_cast<short*>(dest)) = (short)r;
+  *dest = (short)r;
   return true;
 }
 
-bool RE2::Arg::parse_ushort_radix(const char* str,
-                                  size_t n,
-                                  void* dest,
-                                  int radix) {
+template <>
+bool Parse(const char* str, size_t n, unsigned short* dest, int radix) {
   unsigned long r;
-  if (!parse_ulong_radix(str, n, &r, radix)) return false;  // Could not parse
-  if ((unsigned short)r != r) return false;                 // Out of range
+  if (!Parse(str, n, &r, radix)) return false;  // Could not parse
+  if ((unsigned short)r != r) return false;     // Out of range
   if (dest == NULL) return true;
-  *(reinterpret_cast<unsigned short*>(dest)) = (unsigned short)r;
+  *dest = (unsigned short)r;
   return true;
 }
 
-bool RE2::Arg::parse_int_radix(const char* str,
-                               size_t n,
-                               void* dest,
-                               int radix) {
+template <>
+bool Parse(const char* str, size_t n, int* dest, int radix) {
   long r;
-  if (!parse_long_radix(str, n, &r, radix)) return false;  // Could not parse
-  if ((int)r != r) return false;                           // Out of range
+  if (!Parse(str, n, &r, radix)) return false;  // Could not parse
+  if ((int)r != r) return false;                // Out of range
   if (dest == NULL) return true;
-  *(reinterpret_cast<int*>(dest)) = (int)r;
+  *dest = (int)r;
   return true;
 }
 
-bool RE2::Arg::parse_uint_radix(const char* str,
-                                size_t n,
-                                void* dest,
-                                int radix) {
+template <>
+bool Parse(const char* str, size_t n, unsigned int* dest, int radix) {
   unsigned long r;
-  if (!parse_ulong_radix(str, n, &r, radix)) return false;  // Could not parse
-  if ((unsigned int)r != r) return false;                   // Out of range
+  if (!Parse(str, n, &r, radix)) return false;  // Could not parse
+  if ((unsigned int)r != r) return false;       // Out of range
   if (dest == NULL) return true;
-  *(reinterpret_cast<unsigned int*>(dest)) = (unsigned int)r;
+  *dest = (unsigned int)r;
   return true;
 }
 
-bool RE2::Arg::parse_longlong_radix(const char* str,
-                                    size_t n,
-                                    void* dest,
-                                    int radix) {
+template <>
+bool Parse(const char* str, size_t n, long long* dest, int radix) {
   if (n == 0) return false;
   char buf[kMaxNumberLength+1];
   str = TerminateNumber(buf, sizeof buf, str, &n, false);
@@ -1239,14 +1265,12 @@
   if (end != str + n) return false;   // Leftover junk
   if (errno) return false;
   if (dest == NULL) return true;
-  *(reinterpret_cast<long long*>(dest)) = r;
+  *dest = r;
   return true;
 }
 
-bool RE2::Arg::parse_ulonglong_radix(const char* str,
-                                     size_t n,
-                                     void* dest,
-                                     int radix) {
+template <>
+bool Parse(const char* str, size_t n, unsigned long long* dest, int radix) {
   if (n == 0) return false;
   char buf[kMaxNumberLength+1];
   str = TerminateNumber(buf, sizeof buf, str, &n, false);
@@ -1261,68 +1285,11 @@
   if (end != str + n) return false;   // Leftover junk
   if (errno) return false;
   if (dest == NULL) return true;
-  *(reinterpret_cast<unsigned long long*>(dest)) = r;
+  *dest = r;
   return true;
 }
 
-static bool parse_double_float(const char* str, size_t n, bool isfloat,
-                               void* dest) {
-  if (n == 0) return false;
-  static const int kMaxLength = 200;
-  char buf[kMaxLength+1];
-  str = TerminateNumber(buf, sizeof buf, str, &n, true);
-  char* end;
-  errno = 0;
-  double r;
-  if (isfloat) {
-    r = strtof(str, &end);
-  } else {
-    r = strtod(str, &end);
-  }
-  if (end != str + n) return false;   // Leftover junk
-  if (errno) return false;
-  if (dest == NULL) return true;
-  if (isfloat) {
-    *(reinterpret_cast<float*>(dest)) = (float)r;
-  } else {
-    *(reinterpret_cast<double*>(dest)) = r;
-  }
-  return true;
-}
-
-bool RE2::Arg::parse_double(const char* str, size_t n, void* dest) {
-  return parse_double_float(str, n, false, dest);
-}
-
-bool RE2::Arg::parse_float(const char* str, size_t n, void* dest) {
-  return parse_double_float(str, n, true, dest);
-}
-
-#define DEFINE_INTEGER_PARSER(name)                                            \
-  bool RE2::Arg::parse_##name(const char* str, size_t n, void* dest) {         \
-    return parse_##name##_radix(str, n, dest, 10);                             \
-  }                                                                            \
-  bool RE2::Arg::parse_##name##_hex(const char* str, size_t n, void* dest) {   \
-    return parse_##name##_radix(str, n, dest, 16);                             \
-  }                                                                            \
-  bool RE2::Arg::parse_##name##_octal(const char* str, size_t n, void* dest) { \
-    return parse_##name##_radix(str, n, dest, 8);                              \
-  }                                                                            \
-  bool RE2::Arg::parse_##name##_cradix(const char* str, size_t n,              \
-                                       void* dest) {                           \
-    return parse_##name##_radix(str, n, dest, 0);                              \
-  }
-
-DEFINE_INTEGER_PARSER(short)
-DEFINE_INTEGER_PARSER(ushort)
-DEFINE_INTEGER_PARSER(int)
-DEFINE_INTEGER_PARSER(uint)
-DEFINE_INTEGER_PARSER(long)
-DEFINE_INTEGER_PARSER(ulong)
-DEFINE_INTEGER_PARSER(longlong)
-DEFINE_INTEGER_PARSER(ulonglong)
-
-#undef DEFINE_INTEGER_PARSER
+}  // namespace internal
 
 namespace hooks {
 
diff --git a/re2/re2.h b/re2/re2.h
index 89f4fbb..2197b3f 100644
--- a/re2/re2.h
+++ b/re2/re2.h
@@ -208,6 +208,7 @@
 #include <map>
 #include <mutex>
 #include <string>
+#include <type_traits>
 #include <vector>
 
 #if defined(__APPLE__)
@@ -734,32 +735,12 @@
   const Options& options() const { return options_; }
 
   // Argument converters; see below.
-  static inline Arg CRadix(short* x);
-  static inline Arg CRadix(unsigned short* x);
-  static inline Arg CRadix(int* x);
-  static inline Arg CRadix(unsigned int* x);
-  static inline Arg CRadix(long* x);
-  static inline Arg CRadix(unsigned long* x);
-  static inline Arg CRadix(long long* x);
-  static inline Arg CRadix(unsigned long long* x);
-
-  static inline Arg Hex(short* x);
-  static inline Arg Hex(unsigned short* x);
-  static inline Arg Hex(int* x);
-  static inline Arg Hex(unsigned int* x);
-  static inline Arg Hex(long* x);
-  static inline Arg Hex(unsigned long* x);
-  static inline Arg Hex(long long* x);
-  static inline Arg Hex(unsigned long long* x);
-
-  static inline Arg Octal(short* x);
-  static inline Arg Octal(unsigned short* x);
-  static inline Arg Octal(int* x);
-  static inline Arg Octal(unsigned int* x);
-  static inline Arg Octal(long* x);
-  static inline Arg Octal(unsigned long* x);
-  static inline Arg Octal(long long* x);
-  static inline Arg Octal(unsigned long long* x);
+  template <typename T>
+  static Arg CRadix(T* ptr);
+  template <typename T>
+  static Arg Hex(T* ptr);
+  template <typename T>
+  static Arg Octal(T* ptr);
 
  private:
   void Init(const StringPiece& pattern, const Options& options);
@@ -802,130 +783,106 @@
 
 /***** Implementation details *****/
 
-// Hex/Octal/Binary?
+namespace internal {
 
-// Special class for parsing into objects that define a ParseFrom() method
+// Types for which the 3-ary Parse() function template has specializations.
+template <typename T> struct Parse3ary : public std::false_type {};
+template <> struct Parse3ary<void> : public std::true_type {};
+template <> struct Parse3ary<std::string> : public std::true_type {};
+template <> struct Parse3ary<StringPiece> : public std::true_type {};
+template <> struct Parse3ary<char> : public std::true_type {};
+template <> struct Parse3ary<signed char> : public std::true_type {};
+template <> struct Parse3ary<unsigned char> : public std::true_type {};
+template <> struct Parse3ary<float> : public std::true_type {};
+template <> struct Parse3ary<double> : public std::true_type {};
+
 template <typename T>
-class _RE2_MatchObject {
- public:
-  static inline bool Parse(const char* str, size_t n, void* dest) {
-    if (dest == NULL) return true;
-    T* object = reinterpret_cast<T*>(dest);
-    return object->ParseFrom(str, n);
-  }
-};
+bool Parse(const char* str, size_t n, T* dest);
+
+// Types for which the 4-ary Parse() function template has specializations.
+template <typename T> struct Parse4ary : public std::false_type {};
+template <> struct Parse4ary<long> : public std::true_type {};
+template <> struct Parse4ary<unsigned long> : public std::true_type {};
+template <> struct Parse4ary<short> : public std::true_type {};
+template <> struct Parse4ary<unsigned short> : public std::true_type {};
+template <> struct Parse4ary<int> : public std::true_type {};
+template <> struct Parse4ary<unsigned int> : public std::true_type {};
+template <> struct Parse4ary<long long> : public std::true_type {};
+template <> struct Parse4ary<unsigned long long> : public std::true_type {};
+
+template <typename T>
+bool Parse(const char* str, size_t n, T* dest, int radix);
+
+}  // namespace internal
 
 class RE2::Arg {
  public:
-  // Empty constructor so we can declare arrays of RE2::Arg
-  Arg();
+  Arg() : Arg(nullptr) {}
+  Arg(std::nullptr_t ptr)
+      : arg_(ptr), parser_([](const char* str, size_t n, void* dest) -> bool {
+          return true;
+        }) {}
 
-  // Constructor specially designed for NULL arguments
-  Arg(void*);
-  Arg(std::nullptr_t);
+  template <typename T,
+            typename std::enable_if<internal::Parse3ary<T>::value,
+                                    int>::type = 0>
+  Arg(T* ptr)
+      : arg_(ptr), parser_([](const char* str, size_t n, void* dest) -> bool {
+          return internal::Parse(str, n, reinterpret_cast<T*>(dest));
+        }) {}
+
+  template <typename T,
+            typename std::enable_if<internal::Parse4ary<T>::value,
+                                    int>::type = 0>
+  Arg(T* ptr)
+      : arg_(ptr), parser_([](const char* str, size_t n, void* dest) -> bool {
+          return internal::Parse(str, n, reinterpret_cast<T*>(dest), 10);
+        }) {}
+
+  template <typename T,
+            typename std::enable_if<!(internal::Parse3ary<T>::value ||
+                                      internal::Parse4ary<T>::value),
+                                    int>::type = 0>
+  Arg(T* ptr)
+      : arg_(ptr), parser_([](const char* str, size_t n, void* dest) -> bool {
+          if (dest == NULL) return true;
+          return reinterpret_cast<T*>(dest)->ParseFrom(str, n);
+        }) {}
 
   typedef bool (*Parser)(const char* str, size_t n, void* dest);
 
-// Type-specific parsers
-#define MAKE_PARSER(type, name)            \
-  Arg(type* p) : arg_(p), parser_(name) {} \
-  Arg(type* p, Parser parser) : arg_(p), parser_(parser) {}
+  template <typename T>
+  Arg(T* ptr, Parser parser) : arg_(ptr), parser_(parser) {}
 
-  MAKE_PARSER(char,               parse_char)
-  MAKE_PARSER(signed char,        parse_schar)
-  MAKE_PARSER(unsigned char,      parse_uchar)
-  MAKE_PARSER(float,              parse_float)
-  MAKE_PARSER(double,             parse_double)
-  MAKE_PARSER(std::string,        parse_string)
-  MAKE_PARSER(StringPiece,        parse_stringpiece)
-
-  MAKE_PARSER(short,              parse_short)
-  MAKE_PARSER(unsigned short,     parse_ushort)
-  MAKE_PARSER(int,                parse_int)
-  MAKE_PARSER(unsigned int,       parse_uint)
-  MAKE_PARSER(long,               parse_long)
-  MAKE_PARSER(unsigned long,      parse_ulong)
-  MAKE_PARSER(long long,          parse_longlong)
-  MAKE_PARSER(unsigned long long, parse_ulonglong)
-
-#undef MAKE_PARSER
-
-  // Generic constructor templates
-  template <typename T> Arg(T* p)
-      : arg_(p), parser_(_RE2_MatchObject<T>::Parse) { }
-  template <typename T> Arg(T* p, Parser parser)
-      : arg_(p), parser_(parser) { }
-
-  // Parse the data
-  bool Parse(const char* str, size_t n) const;
+  bool Parse(const char* str, size_t n) const {
+    return (*parser_)(str, n, arg_);
+  }
 
  private:
   void*         arg_;
   Parser        parser_;
-
-  static bool parse_null          (const char* str, size_t n, void* dest);
-  static bool parse_char          (const char* str, size_t n, void* dest);
-  static bool parse_schar         (const char* str, size_t n, void* dest);
-  static bool parse_uchar         (const char* str, size_t n, void* dest);
-  static bool parse_float         (const char* str, size_t n, void* dest);
-  static bool parse_double        (const char* str, size_t n, void* dest);
-  static bool parse_string        (const char* str, size_t n, void* dest);
-  static bool parse_stringpiece   (const char* str, size_t n, void* dest);
-
-#define DECLARE_INTEGER_PARSER(name)                                       \
- private:                                                                  \
-  static bool parse_##name(const char* str, size_t n, void* dest);         \
-  static bool parse_##name##_radix(const char* str, size_t n, void* dest,  \
-                                   int radix);                             \
-                                                                           \
- public:                                                                   \
-  static bool parse_##name##_hex(const char* str, size_t n, void* dest);   \
-  static bool parse_##name##_octal(const char* str, size_t n, void* dest); \
-  static bool parse_##name##_cradix(const char* str, size_t n, void* dest);
-
-  DECLARE_INTEGER_PARSER(short)
-  DECLARE_INTEGER_PARSER(ushort)
-  DECLARE_INTEGER_PARSER(int)
-  DECLARE_INTEGER_PARSER(uint)
-  DECLARE_INTEGER_PARSER(long)
-  DECLARE_INTEGER_PARSER(ulong)
-  DECLARE_INTEGER_PARSER(longlong)
-  DECLARE_INTEGER_PARSER(ulonglong)
-
-#undef DECLARE_INTEGER_PARSER
-
 };
 
-inline RE2::Arg::Arg() : arg_(NULL), parser_(parse_null) { }
-inline RE2::Arg::Arg(void* p) : arg_(p), parser_(parse_null) { }
-inline RE2::Arg::Arg(std::nullptr_t p) : arg_(p), parser_(parse_null) { }
-
-inline bool RE2::Arg::Parse(const char* str, size_t n) const {
-  return (*parser_)(str, n, arg_);
+template <typename T>
+inline RE2::Arg RE2::CRadix(T* ptr) {
+  return RE2::Arg(ptr, [](const char* str, size_t n, void* dest) -> bool {
+    return internal::Parse(str, n, reinterpret_cast<T*>(dest), 0);
+  });
 }
 
-// This part of the parser, appropriate only for ints, deals with bases
-#define MAKE_INTEGER_PARSER(type, name)                    \
-  inline RE2::Arg RE2::Hex(type* ptr) {                    \
-    return RE2::Arg(ptr, RE2::Arg::parse_##name##_hex);    \
-  }                                                        \
-  inline RE2::Arg RE2::Octal(type* ptr) {                  \
-    return RE2::Arg(ptr, RE2::Arg::parse_##name##_octal);  \
-  }                                                        \
-  inline RE2::Arg RE2::CRadix(type* ptr) {                 \
-    return RE2::Arg(ptr, RE2::Arg::parse_##name##_cradix); \
-  }
+template <typename T>
+inline RE2::Arg RE2::Hex(T* ptr) {
+  return RE2::Arg(ptr, [](const char* str, size_t n, void* dest) -> bool {
+    return internal::Parse(str, n, reinterpret_cast<T*>(dest), 16);
+  });
+}
 
-MAKE_INTEGER_PARSER(short,              short)
-MAKE_INTEGER_PARSER(unsigned short,     ushort)
-MAKE_INTEGER_PARSER(int,                int)
-MAKE_INTEGER_PARSER(unsigned int,       uint)
-MAKE_INTEGER_PARSER(long,               long)
-MAKE_INTEGER_PARSER(unsigned long,      ulong)
-MAKE_INTEGER_PARSER(long long,          longlong)
-MAKE_INTEGER_PARSER(unsigned long long, ulonglong)
-
-#undef MAKE_INTEGER_PARSER
+template <typename T>
+inline RE2::Arg RE2::Octal(T* ptr) {
+  return RE2::Arg(ptr, [](const char* str, size_t n, void* dest) -> bool {
+    return internal::Parse(str, n, reinterpret_cast<T*>(dest), 8);
+  });
+}
 
 #ifndef SWIG
 // Silence warnings about missing initializers for members of LazyRE2.
diff --git a/re2/testing/re2_arg_test.cc b/re2/testing/re2_arg_test.cc
index 7a38de7..a82f033 100644
--- a/re2/testing/re2_arg_test.cc
+++ b/re2/testing/re2_arg_test.cc
@@ -132,4 +132,24 @@
   PARSE_FOR_TYPE(uint64_t, 5);
 }
 
+TEST(RE2ArgTest, ParseFromTest) {
+  struct {
+    bool ParseFrom(const char* str, size_t n) {
+      LOG(INFO) << "str = " << str << ", n = " << n;
+      return true;
+    }
+  } obj1;
+  RE2::Arg arg1(&obj1);
+  EXPECT_TRUE(arg1.Parse("one", 3));
+
+  struct {
+    bool ParseFrom(const char* str, size_t n) {
+      LOG(INFO) << "str = " << str << ", n = " << n;
+      return false;
+    }
+  } obj2;
+  RE2::Arg arg2(&obj2);
+  EXPECT_FALSE(arg2.Parse("two", 3));
+}
+
 }  // namespace re2