-
Notifications
You must be signed in to change notification settings - Fork 13.3k
[Support] Add format object for interleaved ranges #135517
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-llvm-support Author: Jakub Kuderski (kuhar) ChangesAdd two new format functions for printing ranges: This is meant to improve the ergonomics of printing ranges. Before this patch, we have to either use Before: ArrayRef<Type> types = ...;
ArrayRef<Values> values = ...;
LLVM_DEBUG({
llvm::dbgs() << "Types: ";
llvm::interleave_comma(llvm::dbgs(), types);
llvm::dbgs() << "\n";
llvm::dbgs() << "Values: [";
llvm::interleave_comma(llvm::dbgs(), values);
llvm::dbgs() << "]\n";
}): After: ArrayRef<Type> types = ...;
ArrayRef<Values> values = ...;
LLVM_DEBUG(llvm::dbgs() << "Types: " << interleaved(types) << "\n");
LLVM_DEBUG(llvm::dbgs() << "Values: " << interleaved_array(values) << "\n"); The separator and the prefix/suffix strings are customizable. Full diff: https://github.com/llvm/llvm-project/pull/135517.diff 5 Files Affected:
diff --git a/llvm/include/llvm/Support/Format.h b/llvm/include/llvm/Support/Format.h
index 89b6ae35ba5de..f6c6e449aae6b 100644
--- a/llvm/include/llvm/Support/Format.h
+++ b/llvm/include/llvm/Support/Format.h
@@ -26,6 +26,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/DataTypes.h"
+#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <cstdio>
#include <optional>
@@ -255,6 +256,90 @@ format_bytes_with_ascii(ArrayRef<uint8_t> Bytes,
ByteGroupSize, Upper, true);
}
+/// The base class for printing interleaved ranges. Provides a virtual function
+/// to be used by the '<<' operator in raw_ostream. This class is not intended
+/// to be copied or used in polymorphic containers.
+class InterleavedRangeBase {
+protected:
+ ~InterleavedRangeBase() = default; // Disallow polymorphic deletion.
+ InterleavedRangeBase() = default;
+ InterleavedRangeBase(const InterleavedRangeBase &) = delete; // Disallow copy.
+ virtual void home(); // Out of line virtual method to anchor the vtable.
+
+ /// Appends the interleaved range to the output stream `OS`.
+ virtual void printInterleaved(raw_ostream &OS) const = 0;
+
+ friend class raw_ostream;
+};
+
+/// The concrete class template for interleaved ranges. Supports specifying the
+/// separator and, optionally, the prefix and suffix to be printed surrounding
+/// the range.
+/// Uses the operator '<<' of the range element type for printing. The range
+/// type itself does not have to have an '<<' operator defined.
+template <typename Range>
+class InterleavedRange final : public InterleavedRangeBase {
+ const Range &TheRange;
+ StringRef Separator;
+ StringRef Prefix;
+ StringRef Suffix;
+
+ virtual void printInterleaved(raw_ostream &OS) const override {
+ if (!Prefix.empty())
+ OS << Prefix;
+ llvm::interleave(TheRange, OS, Separator);
+ if (!Suffix.empty())
+ OS << Suffix;
+ }
+
+public:
+ InterleavedRange(const Range &R, StringRef Separator, StringRef Prefix,
+ StringRef Suffix)
+ : TheRange(R), Separator(Separator), Prefix(Prefix), Suffix(Suffix) {}
+
+ std::string str() const {
+ std::string Result;
+ raw_string_ostream Stream(Result);
+ Stream << *this;
+ Stream.flush();
+ return Result;
+ }
+
+ operator std::string() const { return str(); }
+};
+
+/// interleaved - Output range \p R as a sequence of interleaved elements.
+/// Requires the range element type to be printable using
+/// `raw_ostream& operator<<`. The \p Separator and \p Prefix / \p Suffix can be
+/// customized. Examples:
+/// ```c++
+/// SmallVector<int> Vals = {1, 2, 3};
+/// OS << interleaved(Vals); // ==> "1, 2, 3"
+/// OS << interleaved(Vals, ";"); // ==> "1;2;3"
+/// OS << interleaved(Vals, " ", "{", "}"); // ==> "{1 2 3}"
+/// ```
+template <typename Range>
+InterleavedRange<Range> interleaved(const Range &R, StringRef Separator = ", ",
+ StringRef Prefix = "",
+ StringRef Suffix = "") {
+ return {R, Separator, Prefix, Suffix};
+}
+
+/// interleaved - Output range \p R as an array of interleaved elements.
+/// Requires the range element type to be printable using
+/// `raw_ostream& operator<<`. The \p Separator can be customized. Examples:
+/// ```c++
+/// SmallVector<int> Vals = {1, 2, 3};
+/// OS << interleaved_array(Vals); // ==> "[1, 2, 3]"
+/// OS << interleaved_array(Vals, ";"); // ==> "[1;2;3]"
+/// OS << interleaved_array(Vals, " "); // ==> "[1 2 3]"
+/// ```
+template <typename Range>
+InterleavedRange<Range> interleaved_array(const Range &R,
+ StringRef Separator = ", ") {
+ return {R, Separator, "[", "]"};
+}
+
} // end namespace llvm
#endif
diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h
index d3b411590e7fd..f6b2268a94927 100644
--- a/llvm/include/llvm/Support/raw_ostream.h
+++ b/llvm/include/llvm/Support/raw_ostream.h
@@ -29,12 +29,13 @@
namespace llvm {
class Duration;
+template <class T> class [[nodiscard]] Expected;
class formatv_object_base;
class format_object_base;
class FormattedString;
class FormattedNumber;
class FormattedBytes;
-template <class T> class [[nodiscard]] Expected;
+class InterleavedRangeBase;
namespace sys {
namespace fs {
@@ -318,6 +319,9 @@ class raw_ostream {
// Formatted output, see the format_bytes() function in Support/Format.h.
raw_ostream &operator<<(const FormattedBytes &);
+ // Formatted output, see the interleaved() function in Support/Format.h.
+ raw_ostream &operator<<(const InterleavedRangeBase &);
+
/// indent - Insert 'NumSpaces' spaces.
raw_ostream &indent(unsigned NumSpaces);
diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
index e75ddc66b7d16..58d5568cdcea1 100644
--- a/llvm/lib/Support/raw_ostream.cpp
+++ b/llvm/lib/Support/raw_ostream.cpp
@@ -471,6 +471,12 @@ raw_ostream &raw_ostream::operator<<(const FormattedBytes &FB) {
return *this;
}
+raw_ostream &
+raw_ostream::operator<<(const InterleavedRangeBase &InterleavedRange) {
+ InterleavedRange.printInterleaved(*this);
+ return *this;
+}
+
template <char C>
static raw_ostream &write_padding(raw_ostream &OS, unsigned NumChars) {
static const char Chars[] = {C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C,
@@ -555,8 +561,10 @@ void raw_ostream::anchor() {}
//===----------------------------------------------------------------------===//
// Out of line virtual method.
-void format_object_base::home() {
-}
+void format_object_base::home() {}
+
+// Out of line virtual method.
+void InterleavedRangeBase::home() {}
//===----------------------------------------------------------------------===//
// raw_fd_ostream
diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt
index 6c4e7cb689b20..4a12a928af119 100644
--- a/llvm/unittests/Support/CMakeLists.txt
+++ b/llvm/unittests/Support/CMakeLists.txt
@@ -49,6 +49,7 @@ add_llvm_unittest(SupportTests
HashBuilderTest.cpp
IndexedAccessorTest.cpp
InstructionCostTest.cpp
+ InterleavedRangeTest.cpp
JSONTest.cpp
KnownBitsTest.cpp
LEB128Test.cpp
@@ -61,7 +62,7 @@ add_llvm_unittest(SupportTests
MemoryBufferRefTest.cpp
MemoryBufferTest.cpp
MemoryTest.cpp
- MustacheTest.cpp
+ MustacheTest.cpp
ModRefTest.cpp
NativeFormatTests.cpp
OptimizedStructLayoutTest.cpp
diff --git a/llvm/unittests/Support/InterleavedRangeTest.cpp b/llvm/unittests/Support/InterleavedRangeTest.cpp
new file mode 100644
index 0000000000000..53acf36040b67
--- /dev/null
+++ b/llvm/unittests/Support/InterleavedRangeTest.cpp
@@ -0,0 +1,70 @@
+//===- InterleavedRangeTest.cpp - Unit tests for interleaved format -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/Format.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+
+TEST(InterleavedRangeTest, VectorInt) {
+ SmallVector<int> V = {0, 1, 2, 3};
+
+ // First, make sure that the raw print API works as expected.
+ std::string Buff;
+ raw_string_ostream OS(Buff);
+ OS << interleaved(V);
+ EXPECT_EQ("0, 1, 2, 3", Buff);
+ Buff.clear();
+ OS << interleaved_array(V);
+ EXPECT_EQ("[0, 1, 2, 3]", Buff);
+
+ // In the rest of the tests, use `.str()` for convenience.
+ EXPECT_EQ("0, 1, 2, 3", interleaved(V).str());
+ EXPECT_EQ("{{0,1,2,3}}", interleaved(V, ",", "{{", "}}").str());
+ EXPECT_EQ("[0, 1, 2, 3]", interleaved_array(V).str());
+ EXPECT_EQ("[0;1;2;3]", interleaved_array(V, ";").str());
+ EXPECT_EQ("0;1;2;3", interleaved(V, ";").str());
+}
+
+TEST(InterleavedRangeTest, VectorIntEmpty) {
+ SmallVector<int> V = {};
+ EXPECT_EQ("", interleaved(V).str());
+ EXPECT_EQ("{{}}", interleaved(V, ",", "{{", "}}").str());
+ EXPECT_EQ("[]", interleaved_array(V).str());
+ EXPECT_EQ("", interleaved(V, ";").str());
+}
+
+TEST(InterleavedRangeTest, VectorIntOneElem) {
+ SmallVector<int> V = {42};
+ EXPECT_EQ("42", interleaved(V).str());
+ EXPECT_EQ("{{42}}", interleaved(V, ",", "{{", "}}").str());
+ EXPECT_EQ("[42]", interleaved_array(V).str());
+ EXPECT_EQ("42", interleaved(V, ";").str());
+}
+
+struct CustomPrint {
+ int N;
+ friend raw_ostream &operator<<(raw_ostream &OS, const CustomPrint &CP) {
+ OS << "$$" << CP.N << "##";
+ return OS;
+ }
+};
+
+TEST(InterleavedRangeTest, CustomPrint) {
+ CustomPrint V[] = {{3}, {4}, {5}};
+ EXPECT_EQ("$$3##, $$4##, $$5##", interleaved(V).str());
+ EXPECT_EQ("{{$$3##;$$4##;$$5##}}", interleaved(V, ";", "{{", "}}").str());
+ EXPECT_EQ("[$$3##, $$4##, $$5##]", interleaved_array(V).str());
+}
+
+} // namespace
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
Add two new format functions for printing ranges: `interleaved` and `interleaved_array`. This is meant to improve the ergonomics of printing ranges. Before this patch, we have to either use `llvm::interleave` or write a for loop by hand. For example: Before: ```c++ ArrayRef<Type> types = ...; ArrayRef<Values> values = ...; LLVM_DEBUG({ llvm::dbgs() << "Types: "; llvm::interleave_comma(llvm::dbgs(), types); llvm::dbgs() << "\n"; llvm::dbgs() << "Values: ["; llvm::interleave_comma(llvm::dbgs(), values); llvm::dbgs() << "]\n"; }): ``` After: ``` ArrayRef<Type> types = ...; ArrayRef<Values> values = ...; LLVM_DEBUG(llvm::dbgs() << "Types: " << interleaved(types) << "\n"); LLVM_DEBUG(llvm::dbgs() << "Values: " << interleaved_array(values) << "\n"); ``` The separator and the preffix/suffix strings are customizable.
13e4bdb
to
32dcded
Compare
Honestly How would it look to migrate the existing uses of |
Oh, and any precedent for this in the standard library or boost, etc? So we can pick a similar name/interface at least? |
Yes, I want to migrate existing uses of
Yes! This is inspired by https://en.cppreference.com/w/cpp/io/manip/quoted -- the naming is similar on purpose. I'm not aware of anything for ranges though. |
return Result; | ||
} | ||
|
||
operator std::string() const { return str(); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a question - do you want explicit
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
llvm::formatv
doesn't use explicit either and it seems to work fine. Do you see some footguns here? Because this is not a reference/handle/pointer type, I think it's OK.
template <typename Range> | ||
InterleavedRange<Range> interleaved_array(const Range &R, | ||
StringRef Separator = ", ") { | ||
return {R, Separator, "[", "]"}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably a lost cause, but I don't /think/ we use {} to invoke ctors for types that aren't pretty struct-like (exposing their members publicly? some other aspects?), I'd have expected this to be return InterleavedRange(R, Separator, ...);
at least, possibly with the <Range>
in there too.
But... it's probably the case that there is more {}
usage than I'd expect and unlikely to be clawed back in any way, so I'm not sure of the direction I'm suggesting/wouldn't feel strongly about sticking with this code as-is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This pattern is not uncommon in llvm and I don't see anything against it in the coding standards doc, so I'm going to keep this as-is.
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/81/builds/6363 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/146/builds/2717 Here is the relevant piece of the build log for the reference
|
Thanks for adding this. It is very useful for debugging and debug print. I'm already wanting to use it in IREE now. :) |
Add two new format functions for printing ranges: `interleaved` and `interleaved_array`. This is meant to improve the ergonomics of printing ranges. Before this patch, we have to either use `llvm::interleave` or write a for loop by hand. For example: Before: ```c++ ArrayRef<Type> types = ...; ArrayRef<Values> values = ...; LLVM_DEBUG({ llvm::dbgs() << "Types: "; llvm::interleave_comma(llvm::dbgs(), types); llvm::dbgs() << "\n"; llvm::dbgs() << "Values: ["; llvm::interleave_comma(llvm::dbgs(), values); llvm::dbgs() << "]\n"; }): ``` After: ```c++ ArrayRef<Type> types = ...; ArrayRef<Values> values = ...; LLVM_DEBUG(llvm::dbgs() << "Types: " << interleaved(types) << "\n"); LLVM_DEBUG(llvm::dbgs() << "Values: " << interleaved_array(values) << "\n"); ``` The separator and the prefix/suffix strings are customizable.
Clean up printing code by switching to `llvm::interleaved` from llvm#135517.
Clean up printing code by switching to `llvm::interleaved` from llvm#135517.
Clean up printing code by switching to `llvm::interleaved` from llvm#135517.
Clean up printing code by switching to `llvm::interleaved` from llvm#135517.
Clean up printing code by switching to `llvm::interleaved` from llvm#135517.
Clean up printing code by switching to `llvm::interleaved` from llvm#135517. Also make some minor readability & performance fixes.
Clean up printing code by switching to `llvm::interleaved` from #135517.
Clean up printing code by switching to `llvm::interleaved` from #135517. Also make some minor readability & performance fixes.
Clean up printing code by switching to `llvm::interleaved` from llvm/llvm-project#135517.
Clean up printing code by switching to `llvm::interleaved` from llvm/llvm-project#135517. Also make some minor readability & performance fixes.
…ts. NFC. (#136248) Clean up printing code by switching to `llvm::interleaved` from llvm/llvm-project#135517.
* Use `llvm::interleaved` from llvm#135517 to simplify printing * Avoid needless vector allocations
* Use `llvm::interleaved` from #135517 to simplify printing * Avoid needless vector allocations
Use `llvm::interleaved` from llvm#135517 to simplify printing.
Use `llvm::interleaved` from #135517 to simplify printing.
Use `llvm::interleaved` from llvm#135517 to simplify printing.
Use `llvm::interleaved` from llvm#135517 to simplify printing.
Use `llvm::interleaved` from llvm#135517 to simplify printing.
Use `llvm::interleaved` from #135517 to simplify printing.
Add two new format functions for printing ranges:
interleaved
andinterleaved_array
.This is meant to improve the ergonomics of printing ranges. Before this patch, we have to either use
llvm::interleave
or write a for loop by hand. For example:Before:
After:
The separator and the prefix/suffix strings are customizable.