-
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
//===- InterleavedRange.h - Output stream formatting for ranges -----------===// | ||
// | ||
// 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// Implements format objects for printing ranges to output streams. | ||
// For example: | ||
// ```c++ | ||
// ArrayRef<Type> Types = ...; | ||
// OS << "Types: " << interleaved(Types); // ==> "Types: i32, f16, i8" | ||
// ArrayRef<int> Values = ...; | ||
// OS << "Values: " << interleaved_array(Values); // ==> "Values: [1, 2, 3]" | ||
// ``` | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef LLVM_SUPPORT_INTERLEAVED_RANGE_H | ||
#define LLVM_SUPPORT_INTERLEAVED_RANGE_H | ||
|
||
#include "llvm/ADT/STLExtras.h" | ||
#include "llvm/ADT/StringRef.h" | ||
#include "llvm/Support/raw_ostream.h" | ||
|
||
namespace llvm { | ||
|
||
/// Format object class 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 { | ||
const Range &TheRange; | ||
StringRef Separator; | ||
StringRef Prefix; | ||
StringRef Suffix; | ||
|
||
public: | ||
InterleavedRange(const Range &R, StringRef Separator, StringRef Prefix, | ||
StringRef Suffix) | ||
: TheRange(R), Separator(Separator), Prefix(Prefix), Suffix(Suffix) {} | ||
|
||
friend raw_ostream &operator<<(raw_ostream &OS, | ||
const InterleavedRange &Interleaved) { | ||
if (!Interleaved.Prefix.empty()) | ||
OS << Interleaved.Prefix; | ||
llvm::interleave(Interleaved.TheRange, OS, Interleaved.Separator); | ||
if (!Interleaved.Suffix.empty()) | ||
OS << Interleaved.Suffix; | ||
return OS; | ||
} | ||
|
||
std::string str() const { | ||
std::string Result; | ||
raw_string_ostream Stream(Result); | ||
Stream << *this; | ||
Stream.flush(); | ||
return Result; | ||
} | ||
|
||
operator std::string() const { return str(); } | ||
}; | ||
|
||
/// Output range `R` as a sequence of interleaved elements. Requires the range | ||
/// element type to be printable using `raw_ostream& operator<<`. The | ||
/// `Separator` and `Prefix` / `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}; | ||
} | ||
|
||
/// Output range `R` as an array of interleaved elements. Requires the range | ||
/// element type to be printable using `raw_ostream& operator<<`. The | ||
/// `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, "[", "]"}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 But... it's probably the case that there is more There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
} | ||
|
||
} // end namespace llvm | ||
|
||
#endif // LLVM_SUPPORT_INTERLEAVED_RANGE_H |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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/InterleavedRange.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 |
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.