Avoid static_assert in tuple_element for Clang/libc++
authorPeter Griess <pgriess@fb.com>
Thu, 26 Sep 2013 02:07:50 +0000 (21:07 -0500)
committerPeter Griess <pgriess@fb.com>
Tue, 15 Oct 2013 01:46:42 +0000 (18:46 -0700)
Summary:
- Clang/libc++ has a static_assert that blows when tuple_element is
invoked with a tuple of length 0. Unfortunately if we embed this
construct in an enable_if, it still gets evaluated. To work around
this, wrap this in a last_element struct and specialize the 0-element
case there explicitly.

Test Plan:
- fbconfig -r folly && fbmake runtests
- ./configure && make check on Ubuntu/FC/Mac

Reviewed By: andrei.alexandrescu@fb.com

FB internal diff: D998591

folly/Conv.h

index 28ad844b2cc15a7776cec57e4b315fdd57b132ef..75c243c4ff71c4838ac22278acb06579da470198 100644 (file)
@@ -122,6 +122,23 @@ typename std::tuple_element<
   return getLastElement(vs...);
 }
 
+// This class exists to specialize away std::tuple_element in the case where we
+// have 0 template arguments. Without this, Clang/libc++ will blow a
+// static_assert even if tuple_element is protected by an enable_if.
+template <class... Ts>
+struct last_element {
+  typedef typename std::enable_if<
+    sizeof...(Ts) >= 1,
+    typename std::tuple_element<
+      sizeof...(Ts) - 1, std::tuple<Ts...>
+    >::type>::type type;
+};
+
+template <>
+struct last_element<> {
+  typedef void type;
+};
+
 } // namespace detail
 
 /*******************************************************************************
@@ -459,9 +476,8 @@ template <class T, class... Ts>
 typename std::enable_if<sizeof...(Ts) >= 2
   && detail::IsSomeString<
   typename std::remove_pointer<
-    typename std::tuple_element<
-      sizeof...(Ts) - 1, std::tuple<Ts...>
-      >::type>::type>::value>::type
+    typename detail::last_element<Ts...>::type
+  >::type>::value>::type
 toAppend(const T& v, const Ts&... vs) {
   toAppend(v, detail::getLastElement(vs...));
   toAppend(vs...);
@@ -499,9 +515,8 @@ template <class Delimiter, class T, class... Ts>
 typename std::enable_if<sizeof...(Ts) >= 2
   && detail::IsSomeString<
   typename std::remove_pointer<
-    typename std::tuple_element<
-      sizeof...(Ts) - 1, std::tuple<Ts...>
-      >::type>::type>::value>::type
+    typename detail::last_element<Ts...>::type
+  >::type>::value>::type
 toAppendDelim(const Delimiter& delim, const T& v, const Ts&... vs) {
   toAppend(v, delim, detail::getLastElement(vs...));
   toAppendDelim(delim, vs...);