]> granicus.if.org Git - llvm/commitdiff
Add support for formatv to llvm::Twine.
authorZachary Turner <zturner@google.com>
Sat, 17 Dec 2016 00:38:15 +0000 (00:38 +0000)
committerZachary Turner <zturner@google.com>
Sat, 17 Dec 2016 00:38:15 +0000 (00:38 +0000)
Differential Revision: https://reviews.llvm.org/D27835

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@290020 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/ADT/Twine.h
lib/Support/Twine.cpp
unittests/ADT/TwineTest.cpp

index 47caf46f7c23f9cd72efaa8919e89dd2f4166e09..f5f00dcfafe5f8b5c9346f6421ee6d076ed97e03 100644 (file)
@@ -19,6 +19,7 @@
 
 namespace llvm {
 
+  class formatv_object_base;
   class raw_ostream;
 
   /// Twine - A lightweight data structure for efficiently representing the
@@ -102,6 +103,9 @@ namespace llvm {
       /// A pointer to a SmallString instance.
       SmallStringKind,
 
+      /// A pointer to a formatv_object_base instance.
+      FormatvObjectKind,
+
       /// A char value, to render as a character.
       CharKind,
 
@@ -137,6 +141,7 @@ namespace llvm {
       const std::string *stdString;
       const StringRef *stringRef;
       const SmallVectorImpl<char> *smallString;
+      const formatv_object_base *formatvObject;
       char character;
       unsigned int decUI;
       int decI;
@@ -290,6 +295,13 @@ namespace llvm {
       assert(isValid() && "Invalid twine!");
     }
 
+    /// Construct from a formatv_object_base.
+    /*implicit*/ Twine(const formatv_object_base &Fmt)
+        : LHSKind(FormatvObjectKind), RHSKind(EmptyKind) {
+      LHS.formatvObject = &Fmt;
+      assert(isValid() && "Invalid twine!");
+    }
+
     /// Construct from a char.
     explicit Twine(char Val)
       : LHSKind(CharKind), RHSKind(EmptyKind) {
index 5e989fb28b4d60a7fe700552ea5a17f083f4b6b0..465c6e6b8c4cc726f78d303c44bbfb347b0d56d6 100644 (file)
@@ -10,6 +10,7 @@
 #include "llvm/ADT/Twine.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/Support/Debug.h"
+#include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/raw_ostream.h"
 using namespace llvm;
 
@@ -18,6 +19,11 @@ std::string Twine::str() const {
   if (LHSKind == StdStringKind && RHSKind == EmptyKind)
     return *LHS.stdString;
 
+  // If we're storing a formatv_object, we can avoid an extra copy by formatting
+  // it immediately and returning the result.
+  if (LHSKind == FormatvObjectKind && RHSKind == EmptyKind)
+    return LHS.formatvObject->str();
+
   // Otherwise, flatten and copy the contents first.
   SmallString<256> Vec;
   return toStringRef(Vec).str();
@@ -68,6 +74,9 @@ void Twine::printOneChild(raw_ostream &OS, Child Ptr,
   case Twine::SmallStringKind:
     OS << *Ptr.smallString;
     break;
+  case Twine::FormatvObjectKind:
+    OS << *Ptr.formatvObject;
+    break;
   case Twine::CharKind:
     OS << Ptr.character;
     break;
@@ -121,6 +130,9 @@ void Twine::printOneChildRepr(raw_ostream &OS, Child Ptr,
   case Twine::SmallStringKind:
     OS << "smallstring:\"" << *Ptr.smallString << "\"";
     break;
+  case Twine::FormatvObjectKind:
+    OS << "formatv:\"" << *Ptr.formatvObject << "\"";
+    break;
   case Twine::CharKind:
     OS << "char:\"" << Ptr.character << "\"";
     break;
index 9683e97511b6cb583e9e4f977f2fe2a410371837..8d3b6e3d54a093dfcb18540ba5d1768121513c11 100644 (file)
@@ -7,8 +7,10 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "llvm/ADT/Twine.h"
 #include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/FormatAdapters.h"
+#include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/raw_ostream.h"
 #include "gtest/gtest.h"
 using namespace llvm;
@@ -30,6 +32,7 @@ TEST(TwineTest, Construction) {
   EXPECT_EQ("hi", Twine(StringRef(std::string("hi"))).str());
   EXPECT_EQ("hi", Twine(StringRef("hithere", 2)).str());
   EXPECT_EQ("hi", Twine(SmallString<4>("hi")).str());
+  EXPECT_EQ("hi", Twine(formatv("{0}", "hi")).str());
 }
 
 TEST(TwineTest, Numbers) {
@@ -65,6 +68,10 @@ TEST(TwineTest, Concat) {
             repr(Twine().concat(Twine("hi"))));
   EXPECT_EQ("(Twine smallstring:\"hi\" empty)", 
             repr(Twine().concat(Twine(SmallString<5>("hi")))));
+  EXPECT_EQ("(Twine formatv:\"howdy\" empty)",
+            repr(Twine(formatv("howdy")).concat(Twine())));
+  EXPECT_EQ("(Twine formatv:\"howdy\" empty)",
+            repr(Twine().concat(Twine(formatv("howdy")))));
   EXPECT_EQ("(Twine smallstring:\"hey\" cstring:\"there\")", 
             repr(Twine(SmallString<7>("hey")).concat(Twine("there"))));
 
@@ -89,6 +96,25 @@ TEST(TwineTest, toNullTerminatedStringRef) {
   EXPECT_EQ(0, *Twine(SmallString<11>("hello"))
                     .toNullTerminatedStringRef(storage)
                     .end());
+  EXPECT_EQ(0, *Twine(formatv("{0}{1}", "how", "dy"))
+                    .toNullTerminatedStringRef(storage)
+                    .end());
+}
+
+TEST(TwineTest, LazyEvaluation) {
+  struct formatter : FormatAdapter<int> {
+    explicit formatter(int &Count) : Count(Count), FormatAdapter(0) {}
+    int &Count;
+
+    void format(raw_ostream &OS, StringRef Style) { ++Count; }
+  };
+
+  int Count = 0;
+  formatter Formatter(Count);
+  (void)Twine(formatv("{0}", Formatter));
+  EXPECT_EQ(0, Count);
+  (void)Twine(formatv("{0}", Formatter)).str();
+  EXPECT_EQ(1, Count);
 }
 
   // I suppose linking in the entire code generator to add a unit test to check