Skip to content

Commit 32dcded

Browse files
committed
[Support] Add format object for interleaved ranges
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.
1 parent b0acbbe commit 32dcded

File tree

3 files changed

+171
-1
lines changed

3 files changed

+171
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//===- InterleavedRange.h - Output stream formatting for ranges -----------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Implements format objects for printing ranges to output streams.
10+
// For example:
11+
// ```c++
12+
// ArrayRef<Type> Types = ...;
13+
// OS << "Types: " << interleaved(Types); // ==> "Types: i32, f16, i8"
14+
// ArrayRef<int> Values = ...;
15+
// OS << "Values: " << interleaved_array(Values); // ==> "Values: [1, 2, 3]"
16+
// ```
17+
//
18+
//===----------------------------------------------------------------------===//
19+
20+
#ifndef LLVM_SUPPORT_INTERLEAVED_RANGE_H
21+
#define LLVM_SUPPORT_INTERLEAVED_RANGE_H
22+
23+
#include "llvm/ADT/STLExtras.h"
24+
#include "llvm/ADT/StringRef.h"
25+
#include "llvm/Support/raw_ostream.h"
26+
27+
namespace llvm {
28+
29+
/// Format object class for interleaved ranges. Supports specifying the
30+
/// separator and, optionally, the prefix and suffix to be printed surrounding
31+
/// the range.
32+
/// Uses the operator '<<' of the range element type for printing. The range
33+
/// type itself does not have to have an '<<' operator defined.
34+
template <typename Range> class InterleavedRange {
35+
const Range &TheRange;
36+
StringRef Separator;
37+
StringRef Prefix;
38+
StringRef Suffix;
39+
40+
public:
41+
InterleavedRange(const Range &R, StringRef Separator, StringRef Prefix,
42+
StringRef Suffix)
43+
: TheRange(R), Separator(Separator), Prefix(Prefix), Suffix(Suffix) {}
44+
45+
friend raw_ostream &operator<<(raw_ostream &OS,
46+
const InterleavedRange &Interleaved) {
47+
if (!Interleaved.Prefix.empty())
48+
OS << Interleaved.Prefix;
49+
llvm::interleave(Interleaved.TheRange, OS, Interleaved.Separator);
50+
if (!Interleaved.Suffix.empty())
51+
OS << Interleaved.Suffix;
52+
return OS;
53+
}
54+
55+
std::string str() const {
56+
std::string Result;
57+
raw_string_ostream Stream(Result);
58+
Stream << *this;
59+
Stream.flush();
60+
return Result;
61+
}
62+
63+
operator std::string() const { return str(); }
64+
};
65+
66+
/// Output range `R` as a sequence of interleaved elements. Requires the range
67+
/// element type to be printable using `raw_ostream& operator<<`. The
68+
/// `Separator` and `Prefix` / `Suffix` can be customized. Examples:
69+
/// ```c++
70+
/// SmallVector<int> Vals = {1, 2, 3};
71+
/// OS << interleaved(Vals); // ==> "1, 2, 3"
72+
/// OS << interleaved(Vals, ";"); // ==> "1;2;3"
73+
/// OS << interleaved(Vals, " ", "{", "}"); // ==> "{1 2 3}"
74+
/// ```
75+
template <typename Range>
76+
InterleavedRange<Range> interleaved(const Range &R, StringRef Separator = ", ",
77+
StringRef Prefix = "",
78+
StringRef Suffix = "") {
79+
return {R, Separator, Prefix, Suffix};
80+
}
81+
82+
/// Output range `R` as an array of interleaved elements. Requires the range
83+
/// element type to be printable using `raw_ostream& operator<<`. The
84+
/// `Separator` can be customized. Examples:
85+
/// ```c++
86+
/// SmallVector<int> Vals = {1, 2, 3};
87+
/// OS << interleaved_array(Vals); // ==> "[1, 2, 3]"
88+
/// OS << interleaved_array(Vals, ";"); // ==> "[1;2;3]"
89+
/// OS << interleaved_array(Vals, " "); // ==> "[1 2 3]"
90+
/// ```
91+
template <typename Range>
92+
InterleavedRange<Range> interleaved_array(const Range &R,
93+
StringRef Separator = ", ") {
94+
return {R, Separator, "[", "]"};
95+
}
96+
97+
} // end namespace llvm
98+
99+
#endif // LLVM_SUPPORT_INTERLEAVED_RANGE_H

llvm/unittests/Support/CMakeLists.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ add_llvm_unittest(SupportTests
4949
HashBuilderTest.cpp
5050
IndexedAccessorTest.cpp
5151
InstructionCostTest.cpp
52+
InterleavedRangeTest.cpp
5253
JSONTest.cpp
5354
KnownBitsTest.cpp
5455
LEB128Test.cpp
@@ -61,7 +62,7 @@ add_llvm_unittest(SupportTests
6162
MemoryBufferRefTest.cpp
6263
MemoryBufferTest.cpp
6364
MemoryTest.cpp
64-
MustacheTest.cpp
65+
MustacheTest.cpp
6566
ModRefTest.cpp
6667
NativeFormatTests.cpp
6768
OptimizedStructLayoutTest.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//===- InterleavedRangeTest.cpp - Unit tests for interleaved format -------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm/ADT/SmallVector.h"
10+
#include "llvm/Support/InterleavedRange.h"
11+
#include "llvm/Support/raw_ostream.h"
12+
#include "gmock/gmock.h"
13+
#include "gtest/gtest.h"
14+
15+
using namespace llvm;
16+
17+
namespace {
18+
19+
TEST(InterleavedRangeTest, VectorInt) {
20+
SmallVector<int> V = {0, 1, 2, 3};
21+
22+
// First, make sure that the raw print API works as expected.
23+
std::string Buff;
24+
raw_string_ostream OS(Buff);
25+
OS << interleaved(V);
26+
EXPECT_EQ("0, 1, 2, 3", Buff);
27+
Buff.clear();
28+
OS << interleaved_array(V);
29+
EXPECT_EQ("[0, 1, 2, 3]", Buff);
30+
31+
// In the rest of the tests, use `.str()` for convenience.
32+
EXPECT_EQ("0, 1, 2, 3", interleaved(V).str());
33+
EXPECT_EQ("{{0,1,2,3}}", interleaved(V, ",", "{{", "}}").str());
34+
EXPECT_EQ("[0, 1, 2, 3]", interleaved_array(V).str());
35+
EXPECT_EQ("[0;1;2;3]", interleaved_array(V, ";").str());
36+
EXPECT_EQ("0;1;2;3", interleaved(V, ";").str());
37+
}
38+
39+
TEST(InterleavedRangeTest, VectorIntEmpty) {
40+
SmallVector<int> V = {};
41+
EXPECT_EQ("", interleaved(V).str());
42+
EXPECT_EQ("{{}}", interleaved(V, ",", "{{", "}}").str());
43+
EXPECT_EQ("[]", interleaved_array(V).str());
44+
EXPECT_EQ("", interleaved(V, ";").str());
45+
}
46+
47+
TEST(InterleavedRangeTest, VectorIntOneElem) {
48+
SmallVector<int> V = {42};
49+
EXPECT_EQ("42", interleaved(V).str());
50+
EXPECT_EQ("{{42}}", interleaved(V, ",", "{{", "}}").str());
51+
EXPECT_EQ("[42]", interleaved_array(V).str());
52+
EXPECT_EQ("42", interleaved(V, ";").str());
53+
}
54+
55+
struct CustomPrint {
56+
int N;
57+
friend raw_ostream &operator<<(raw_ostream &OS, const CustomPrint &CP) {
58+
OS << "$$" << CP.N << "##";
59+
return OS;
60+
}
61+
};
62+
63+
TEST(InterleavedRangeTest, CustomPrint) {
64+
CustomPrint V[] = {{3}, {4}, {5}};
65+
EXPECT_EQ("$$3##, $$4##, $$5##", interleaved(V).str());
66+
EXPECT_EQ("{{$$3##;$$4##;$$5##}}", interleaved(V, ";", "{{", "}}").str());
67+
EXPECT_EQ("[$$3##, $$4##, $$5##]", interleaved_array(V).str());
68+
}
69+
70+
} // namespace

0 commit comments

Comments
 (0)