diff --git a/llvm/include/llvm/Support/InterleavedRange.h b/llvm/include/llvm/Support/InterleavedRange.h new file mode 100644 index 0000000000000..4e70028504806 --- /dev/null +++ b/llvm/include/llvm/Support/InterleavedRange.h @@ -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 Types = ...; +// OS << "Types: " << interleaved(Types); // ==> "Types: i32, f16, i8" +// ArrayRef 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 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 Vals = {1, 2, 3}; +/// OS << interleaved(Vals); // ==> "1, 2, 3" +/// OS << interleaved(Vals, ";"); // ==> "1;2;3" +/// OS << interleaved(Vals, " ", "{", "}"); // ==> "{1 2 3}" +/// ``` +template +InterleavedRange 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 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 +InterleavedRange interleaved_array(const Range &R, + StringRef Separator = ", ") { + return {R, Separator, "[", "]"}; +} + +} // end namespace llvm + +#endif // LLVM_SUPPORT_INTERLEAVED_RANGE_H 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..8640b81fe8ad8 --- /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/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 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 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 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