[llvm] r336412 - [Support] Make support types more easily printable.

Sam McCall via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 5 22:45:45 PDT 2018


Author: sammccall
Date: Thu Jul  5 22:45:45 2018
New Revision: 336412

URL: http://llvm.org/viewvc/llvm-project?rev=336412&view=rev
Log:
[Support] Make support types more easily printable.

Summary:
Error's new operator<< is the first way to print an error without consuming it.

formatv() can now print objects with an operator<< that works with raw_ostream.

Reviewers: bkramer

Subscribers: mgorny, llvm-commits

Differential Revision: https://reviews.llvm.org/D48966

Modified:
    llvm/trunk/include/llvm/Support/Error.h
    llvm/trunk/include/llvm/Support/FormatVariadic.h
    llvm/trunk/include/llvm/Support/FormatVariadicDetails.h
    llvm/trunk/unittests/Support/ErrorTest.cpp
    llvm/trunk/unittests/Support/FormatVariadicTest.cpp

Modified: llvm/trunk/include/llvm/Support/Error.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/Error.h?rev=336412&r1=336411&r2=336412&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/Error.h (original)
+++ llvm/trunk/include/llvm/Support/Error.h Thu Jul  5 22:45:45 2018
@@ -302,6 +302,14 @@ private:
     return Tmp;
   }
 
+  friend raw_ostream &operator<<(raw_ostream &OS, const Error &E) {
+    if (auto P = E.getPtr())
+      P->log(OS);
+    else
+      OS << "success";
+    return OS;
+  }
+
   ErrorInfoBase *Payload = nullptr;
 };
 

Modified: llvm/trunk/include/llvm/Support/FormatVariadic.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/FormatVariadic.h?rev=336412&r1=336411&r2=336412&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/FormatVariadic.h (original)
+++ llvm/trunk/include/llvm/Support/FormatVariadic.h Thu Jul  5 22:45:45 2018
@@ -237,6 +237,8 @@ public:
 //      for type T containing a method whose signature is:
 //      void format(const T &Obj, raw_ostream &Stream, StringRef Options)
 //      Then this method is invoked as described in Step 1.
+//   3. If an appropriate operator<< for raw_ostream exists, it will be used.
+//      For this to work, (raw_ostream& << const T&) must return raw_ostream&.
 //
 // If a match cannot be found through either of the above methods, a compiler
 // error is generated.
@@ -258,13 +260,6 @@ inline auto formatv(const char *Fmt, Ts
       std::make_tuple(detail::build_format_adapter(std::forward<Ts>(Vals))...));
 }
 
-// Allow a formatv_object to be formatted (no options supported).
-template <typename T> struct format_provider<formatv_object<T>> {
-  static void format(const formatv_object<T> &V, raw_ostream &OS, StringRef) {
-    OS << V;
-  }
-};
-
 } // end namespace llvm
 
 #endif // LLVM_SUPPORT_FORMATVARIADIC_H

Modified: llvm/trunk/include/llvm/Support/FormatVariadicDetails.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/FormatVariadicDetails.h?rev=336412&r1=336411&r2=336412&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/FormatVariadicDetails.h (original)
+++ llvm/trunk/include/llvm/Support/FormatVariadicDetails.h Thu Jul  5 22:45:45 2018
@@ -38,6 +38,17 @@ public:
   }
 };
 
+template <typename T>
+class stream_operator_format_adapter : public format_adapter {
+  T Item;
+
+public:
+  explicit stream_operator_format_adapter(T &&Item)
+      : Item(std::forward<T>(Item)) {}
+
+  void format(llvm::raw_ostream &S, StringRef Options) override { S << Item; }
+};
+
 template <typename T> class missing_format_adapter;
 
 // Test if format_provider<T> is defined on T and contains a member function
@@ -59,6 +70,23 @@ public:
       (sizeof(test<llvm::format_provider<Decayed>>(nullptr)) == 1);
 };
 
+// Test if raw_ostream& << T -> raw_ostream& is findable via ADL.
+template <class T> class has_StreamOperator {
+public:
+  using ConstRefT = const typename std::decay<T>::type &;
+
+  template <typename U>
+  static char test(typename std::enable_if<
+                   std::is_same<decltype(std::declval<llvm::raw_ostream &>()
+                                         << std::declval<U>()),
+                                llvm::raw_ostream &>::value,
+                   int *>::type);
+
+  template <typename U> static double test(...);
+
+  static bool const value = (sizeof(test<ConstRefT>(nullptr)) == 1);
+};
+
 // Simple template that decides whether a type T should use the member-function
 // based format() invocation.
 template <typename T>
@@ -77,15 +105,24 @@ struct uses_format_provider
           bool, !uses_format_member<T>::value && has_FormatProvider<T>::value> {
 };
 
+// Simple template that decides whether a type T should use the operator<<
+// based format() invocation.  This takes last priority.
+template <typename T>
+struct uses_stream_operator
+    : public std::integral_constant<bool, !uses_format_member<T>::value &&
+                                              !uses_format_provider<T>::value &&
+                                              has_StreamOperator<T>::value> {};
+
 // Simple template that decides whether a type T has neither a member-function
 // nor format_provider based implementation that it can use.  Mostly used so
 // that the compiler spits out a nice diagnostic when a type with no format
 // implementation can be located.
 template <typename T>
 struct uses_missing_provider
-    : public std::integral_constant<bool,
-                                    !uses_format_member<T>::value &&
-                                        !uses_format_provider<T>::value> {};
+    : public std::integral_constant<bool, !uses_format_member<T>::value &&
+                                              !uses_format_provider<T>::value &&
+                                              !uses_stream_operator<T>::value> {
+};
 
 template <typename T>
 typename std::enable_if<uses_format_member<T>::value, T>::type
@@ -101,6 +138,13 @@ build_format_adapter(T &&Item) {
 }
 
 template <typename T>
+typename std::enable_if<uses_stream_operator<T>::value,
+                        stream_operator_format_adapter<T>>::type
+build_format_adapter(T &&Item) {
+  return stream_operator_format_adapter<T>(std::forward<T>(Item));
+}
+
+template <typename T>
 typename std::enable_if<uses_missing_provider<T>::value,
                         missing_format_adapter<T>>::type
 build_format_adapter(T &&Item) {

Modified: llvm/trunk/unittests/Support/ErrorTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Support/ErrorTest.cpp?rev=336412&r1=336411&r2=336412&view=diff
==============================================================================
--- llvm/trunk/unittests/Support/ErrorTest.cpp (original)
+++ llvm/trunk/unittests/Support/ErrorTest.cpp Thu Jul  5 22:45:45 2018
@@ -32,7 +32,7 @@ public:
 
   // Log this error to a stream.
   void log(raw_ostream &OS) const override {
-    OS << "CustomError { " << getInfo() << "}";
+    OS << "CustomError {" << getInfo() << "}";
   }
 
   std::error_code convertToErrorCode() const override {
@@ -702,25 +702,44 @@ TEST(Error, ErrorMessage) {
   EXPECT_EQ(toString(Error::success()).compare(""), 0);
 
   Error E1 = make_error<CustomError>(0);
-  EXPECT_EQ(toString(std::move(E1)).compare("CustomError { 0}"), 0);
+  EXPECT_EQ(toString(std::move(E1)).compare("CustomError {0}"), 0);
 
   Error E2 = make_error<CustomError>(0);
   handleAllErrors(std::move(E2), [](const CustomError &CE) {
-    EXPECT_EQ(CE.message().compare("CustomError { 0}"), 0);
+    EXPECT_EQ(CE.message().compare("CustomError {0}"), 0);
   });
 
   Error E3 = joinErrors(make_error<CustomError>(0), make_error<CustomError>(1));
   EXPECT_EQ(toString(std::move(E3))
-                .compare("CustomError { 0}\n"
-                         "CustomError { 1}"),
+                .compare("CustomError {0}\n"
+                         "CustomError {1}"),
             0);
 }
 
+TEST(Error, Stream) {
+  {
+    Error OK = Error::success();
+    std::string Buf;
+    llvm::raw_string_ostream S(Buf);
+    S << OK;
+    EXPECT_EQ("success", S.str());
+    consumeError(std::move(OK));
+  }
+  {
+    Error E1 = make_error<CustomError>(0);
+    std::string Buf;
+    llvm::raw_string_ostream S(Buf);
+    S << E1;
+    EXPECT_EQ("CustomError {0}", S.str());
+    consumeError(std::move(E1));
+  }
+}
+
 TEST(Error, ErrorMatchers) {
   EXPECT_THAT_ERROR(Error::success(), Succeeded());
   EXPECT_NONFATAL_FAILURE(
       EXPECT_THAT_ERROR(make_error<CustomError>(0), Succeeded()),
-      "Expected: succeeded\n  Actual: failed  (CustomError { 0})");
+      "Expected: succeeded\n  Actual: failed  (CustomError {0})");
 
   EXPECT_THAT_ERROR(make_error<CustomError>(0), Failed());
   EXPECT_NONFATAL_FAILURE(EXPECT_THAT_ERROR(Error::success(), Failed()),
@@ -748,14 +767,14 @@ TEST(Error, ErrorMatchers) {
           Failed<CustomError>(testing::Property(&CustomError::getInfo, 1))),
       "Expected: failed with Error of given type and the error is an object "
       "whose given property is equal to 1\n"
-      "  Actual: failed  (CustomError { 0})");
+      "  Actual: failed  (CustomError {0})");
   EXPECT_THAT_ERROR(make_error<CustomError>(0), Failed<ErrorInfoBase>());
 
   EXPECT_THAT_EXPECTED(Expected<int>(0), Succeeded());
   EXPECT_NONFATAL_FAILURE(
       EXPECT_THAT_EXPECTED(Expected<int>(make_error<CustomError>(0)),
                            Succeeded()),
-      "Expected: succeeded\n  Actual: failed  (CustomError { 0})");
+      "Expected: succeeded\n  Actual: failed  (CustomError {0})");
 
   EXPECT_THAT_EXPECTED(Expected<int>(make_error<CustomError>(0)), Failed());
   EXPECT_NONFATAL_FAILURE(
@@ -767,7 +786,7 @@ TEST(Error, ErrorMatchers) {
       EXPECT_THAT_EXPECTED(Expected<int>(make_error<CustomError>(0)),
                            HasValue(0)),
       "Expected: succeeded with value (is equal to 0)\n"
-      "  Actual: failed  (CustomError { 0})");
+      "  Actual: failed  (CustomError {0})");
   EXPECT_NONFATAL_FAILURE(
       EXPECT_THAT_EXPECTED(Expected<int>(1), HasValue(0)),
       "Expected: succeeded with value (is equal to 0)\n"
@@ -787,7 +806,7 @@ TEST(Error, ErrorMatchers) {
       EXPECT_THAT_EXPECTED(Expected<int>(make_error<CustomError>(0)),
                            HasValue(testing::Gt(1))),
       "Expected: succeeded with value (is > 1)\n"
-      "  Actual: failed  (CustomError { 0})");
+      "  Actual: failed  (CustomError {0})");
 }
 
 } // end anon namespace

Modified: llvm/trunk/unittests/Support/FormatVariadicTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Support/FormatVariadicTest.cpp?rev=336412&r1=336411&r2=336412&view=diff
==============================================================================
--- llvm/trunk/unittests/Support/FormatVariadicTest.cpp (original)
+++ llvm/trunk/unittests/Support/FormatVariadicTest.cpp Thu Jul  5 22:45:45 2018
@@ -671,3 +671,12 @@ TEST(FormatVariadicTest, CopiesAndMoves)
   EXPECT_EQ(0, R.Copied);
   EXPECT_EQ(0, R.Moved);
 }
+
+namespace adl {
+struct X {};
+raw_ostream &operator<<(raw_ostream &OS, const X &) { return OS << "X"; }
+} // namespace adl
+TEST(FormatVariadicTest, FormatStreamable) {
+  adl::X X;
+  EXPECT_EQ("X", formatv("{0}", X).str());
+}




More information about the llvm-commits mailing list