From 2a25e2bf4ddd2030255279e8382ca4e8722b1825 Mon Sep 17 00:00:00 2001
From: Alexey Ochapov <alexez@alexez.com>
Date: Sat, 30 Jan 2021 18:42:58 +0300
Subject: [PATCH] Make ranges-test available with C++11 (#2114)

* make ranges-test available with C++11, fix problem with some gcc versions

* potentially fix build for MSVC 19.10, a bit reorganizing in test
---
 include/fmt/format.h |   6 +-
 include/fmt/ranges.h |   2 +-
 test/ranges-test.cc  | 180 +++++++++++++++++++++++--------------------
 3 files changed, 99 insertions(+), 89 deletions(-)

diff --git a/include/fmt/format.h b/include/fmt/format.h
index df53308169e1..06c2f63f4fd7 100644
--- a/include/fmt/format.h
+++ b/include/fmt/format.h
@@ -381,7 +381,7 @@ template <typename T> inline T* make_checked(T* p, size_t) { return p; }
 #endif
 
 template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
-#if FMT_CLANG_VERSION
+#if FMT_CLANG_VERSION >= 307
 __attribute__((no_sanitize("undefined")))
 #endif
 inline checked_ptr<typename Container::value_type>
@@ -941,8 +941,8 @@ template <typename T = void> struct FMT_EXTERN_TEMPLATE_API basic_data {
   static const char reset_color[5];
   static const wchar_t wreset_color[5];
   static const char signs[];
-  static constexpr const char left_padding_shifts[] = {31, 31, 0, 1, 0};
-  static constexpr const char right_padding_shifts[] = {0, 31, 0, 1, 0};
+  static constexpr const char left_padding_shifts[5] = {31, 31, 0, 1, 0};
+  static constexpr const char right_padding_shifts[5] = {0, 31, 0, 1, 0};
 
   // DEPRECATED! These are for ABI compatibility.
   static const uint32_t zero_or_powers_of_10_32[];
diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h
index 7b58f4e1b1a4..0ebf96526442 100644
--- a/include/fmt/ranges.h
+++ b/include/fmt/ranges.h
@@ -368,7 +368,7 @@ template <typename Char, typename... T> struct tuple_arg_join : detail::view {
   basic_string_view<Char> sep;
 
   tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
-      : tuple{t}, sep{s} {}
+      : tuple(t), sep{s} {}
 };
 
 template <typename Char, typename... T>
diff --git a/test/ranges-test.cc b/test/ranges-test.cc
index 802f4ab201f7..30ed97f21233 100644
--- a/test/ranges-test.cc
+++ b/test/ranges-test.cc
@@ -11,17 +11,23 @@
 
 #include "fmt/ranges.h"
 
+#include <array>
+#include <map>
+#include <string>
+#include <vector>
+
 #include "gtest.h"
 
-// Check if  'if constexpr' is supported.
-#if (__cplusplus > 201402L) || \
-    (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910)
+#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 601
+#  define FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
+#endif
 
-#  include <array>
-#  include <map>
-#  include <string>
-#  include <vector>
+#if !FMT_MSC_VER || FMT_MSC_VER > 1910
+#  define FMT_RANGES_TEST_ENABLE_JOIN
+#  define FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
+#endif
 
+#ifdef FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
 TEST(RangesTest, FormatArray) {
   int32_t ia[] = {1, 2, 3, 5, 7, 11};
   auto iaf = fmt::format("{}", ia);
@@ -34,6 +40,12 @@ TEST(RangesTest, Format2dArray) {
   EXPECT_EQ("{{1, 2}, {3, 5}, {7, 11}}", iaf);
 }
 
+TEST(RangesTest, FormatArrayOfLiterals) {
+  const char* aol[] = {"1234", "abcd"};
+  EXPECT_EQ("{\"1234\", \"abcd\"}", fmt::format("{}", aol));
+}
+#endif  // FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
+
 TEST(RangesTest, FormatVector) {
   std::vector<int32_t> iv{1, 2, 3, 5, 7, 11};
   auto ivf = fmt::format("{}", iv);
@@ -51,11 +63,6 @@ TEST(RangesTest, FormatMap) {
   EXPECT_EQ("{(\"one\", 1), (\"two\", 2)}", fmt::format("{}", simap));
 }
 
-TEST(RangesTest, FormatArrayOfLiterals) {
-  const char* aol[] = {"1234", "abcd"};
-  EXPECT_EQ("{\"1234\", \"abcd\"}", fmt::format("{}", aol));
-}
-
 TEST(RangesTest, FormatPair) {
   std::pair<int64_t, float> pa1{42, 1.5f};
   EXPECT_EQ("(42, 1.5)", fmt::format("{}", pa1));
@@ -68,43 +75,21 @@ TEST(RangesTest, FormatTuple) {
   EXPECT_EQ("()", fmt::format("{}", std::tuple<>()));
 }
 
-TEST(RangesTest, JoinTuple) {
-  // Value tuple args
-  std::tuple<char, int, float> t1 = std::make_tuple('a', 1, 2.0f);
-  EXPECT_EQ("(a, 1, 2)", fmt::format("({})", fmt::join(t1, ", ")));
-
-  // Testing lvalue tuple args
-  int x = 4;
-  std::tuple<char, int&> t2{'b', x};
-  EXPECT_EQ("b + 4", fmt::format("{}", fmt::join(t2, " + ")));
-
-  // Empty tuple
-  std::tuple<> t3;
-  EXPECT_EQ("", fmt::format("{}", fmt::join(t3, "|")));
-
-  // Single element tuple
-  std::tuple<float> t4{4.0f};
-  EXPECT_EQ("4", fmt::format("{}", fmt::join(t4, "/")));
-}
-
-TEST(RangesTest, JoinInitializerList) {
-  EXPECT_EQ("1, 2, 3", fmt::format("{}", fmt::join({1, 2, 3}, ", ")));
-  EXPECT_EQ("fmt rocks !",
-            fmt::format("{}", fmt::join({"fmt", "rocks", "!"}, " ")));
-}
-
+#ifdef FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
 struct my_struct {
   int32_t i;
   std::string str;  // can throw
-  template <size_t N> decltype(auto) get() const noexcept {
-    if constexpr (N == 0)
-      return i;
-    else if constexpr (N == 1)
-      return fmt::string_view{str};
+  template <size_t N> fmt::enable_if_t<N == 0, int32_t> get() const noexcept {
+    return i;
+  }
+  template <size_t N>
+  fmt::enable_if_t<N == 1, fmt::string_view> get() const noexcept {
+    return {str};
   }
 };
 
-template <size_t N> decltype(auto) get(const my_struct& s) noexcept {
+template <size_t N>
+auto get(const my_struct& s) noexcept -> decltype(s.get<N>()) {
   return s.get<N>();
 }
 
@@ -122,10 +107,11 @@ TEST(RangesTest, FormatStruct) {
   my_struct mst{13, "my struct"};
   EXPECT_EQ("(13, \"my struct\")", fmt::format("{}", mst));
 }
+#endif  // FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
 
 TEST(RangesTest, FormatTo) {
   char buf[10];
-  auto end = fmt::format_to(buf, "{}", std::vector{1, 2, 3});
+  auto end = fmt::format_to(buf, "{}", std::vector<int>{1, 2, 3});
   *end = '\0';
   EXPECT_STREQ(buf, "{1, 2, 3}");
 }
@@ -141,9 +127,6 @@ TEST(RangesTest, PathLike) {
   EXPECT_FALSE((fmt::is_range<path_like, char>::value));
 }
 
-#endif  // (__cplusplus > 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >
-        // 201402L && _MSC_VER >= 1910)
-
 #ifdef FMT_USE_STRING_VIEW
 struct string_like {
   const char* begin();
@@ -157,23 +140,6 @@ TEST(RangesTest, FormatStringLike) {
 }
 #endif  // FMT_USE_STRING_VIEW
 
-struct zstring_sentinel {};
-
-bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; }
-bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; }
-
-struct zstring {
-  const char* p;
-  const char* begin() const { return p; }
-  zstring_sentinel end() const { return {}; }
-};
-
-TEST(RangesTest, JoinSentinel) {
-  zstring hello{"hello"};
-  EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format("{}", hello));
-  EXPECT_EQ("h_e_l_l_o", fmt::format("{}", fmt::join(hello, "_")));
-}
-
 // A range that provides non-const only begin()/end() to test fmt::join handles
 // that
 //
@@ -212,27 +178,6 @@ template <typename T> class noncopyable_range {
   const_iterator end() const { return vec.end(); }
 };
 
-TEST(RangesTest, JoinRange) {
-  noncopyable_range<int> w(3u, 0);
-  EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(w, ",")));
-  EXPECT_EQ("0,0,0",
-            fmt::format("{}", fmt::join(noncopyable_range<int>(3u, 0), ",")));
-
-  non_const_only_range<int> x(3u, 0);
-  EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(x, ",")));
-  EXPECT_EQ(
-      "0,0,0",
-      fmt::format("{}", fmt::join(non_const_only_range<int>(3u, 0), ",")));
-
-  std::vector<int> y(3u, 0);
-  EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(y, ",")));
-  EXPECT_EQ("0,0,0",
-            fmt::format("{}", fmt::join(std::vector<int>(3u, 0), ",")));
-
-  const std::vector<int> z(3u, 0);
-  EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(z, ",")));
-}
-
 TEST(RangesTest, Range) {
   noncopyable_range<int> w(3u, 0);
   EXPECT_EQ("{0, 0, 0}", fmt::format("{}", w));
@@ -258,3 +203,68 @@ TEST(RangesTest, UnformattableRange) {
                                    fmt::format_context>::value));
 }
 #endif
+
+#ifdef FMT_RANGES_TEST_ENABLE_JOIN
+TEST(RangesTest, JoinTuple) {
+  // Value tuple args
+  std::tuple<char, int, float> t1 = std::make_tuple('a', 1, 2.0f);
+  EXPECT_EQ("(a, 1, 2)", fmt::format("({})", fmt::join(t1, ", ")));
+
+  // Testing lvalue tuple args
+  int x = 4;
+  std::tuple<char, int&> t2{'b', x};
+  EXPECT_EQ("b + 4", fmt::format("{}", fmt::join(t2, " + ")));
+
+  // Empty tuple
+  std::tuple<> t3;
+  EXPECT_EQ("", fmt::format("{}", fmt::join(t3, "|")));
+
+  // Single element tuple
+  std::tuple<float> t4{4.0f};
+  EXPECT_EQ("4", fmt::format("{}", fmt::join(t4, "/")));
+}
+
+TEST(RangesTest, JoinInitializerList) {
+  EXPECT_EQ("1, 2, 3", fmt::format("{}", fmt::join({1, 2, 3}, ", ")));
+  EXPECT_EQ("fmt rocks !",
+            fmt::format("{}", fmt::join({"fmt", "rocks", "!"}, " ")));
+}
+
+struct zstring_sentinel {};
+
+bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; }
+bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; }
+
+struct zstring {
+  const char* p;
+  const char* begin() const { return p; }
+  zstring_sentinel end() const { return {}; }
+};
+
+TEST(RangesTest, JoinSentinel) {
+  zstring hello{"hello"};
+  EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format("{}", hello));
+  EXPECT_EQ("h_e_l_l_o", fmt::format("{}", fmt::join(hello, "_")));
+}
+
+TEST(RangesTest, JoinRange) {
+  noncopyable_range<int> w(3u, 0);
+  EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(w, ",")));
+  EXPECT_EQ("0,0,0",
+            fmt::format("{}", fmt::join(noncopyable_range<int>(3u, 0), ",")));
+
+  non_const_only_range<int> x(3u, 0);
+  EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(x, ",")));
+  EXPECT_EQ(
+      "0,0,0",
+      fmt::format("{}", fmt::join(non_const_only_range<int>(3u, 0), ",")));
+
+  std::vector<int> y(3u, 0);
+  EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(y, ",")));
+  EXPECT_EQ("0,0,0",
+            fmt::format("{}", fmt::join(std::vector<int>(3u, 0), ",")));
+
+  const std::vector<int> z(3u, 0);
+  EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(z, ",")));
+}
+#endif  // FMT_RANGES_TEST_ENABLE_JOIN
\ No newline at end of file