diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h --- a/llvm/include/llvm/Support/raw_ostream.h +++ b/llvm/include/llvm/Support/raw_ostream.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -714,6 +715,66 @@ ~buffer_unique_ostream() override { *OS << str(); } }; +/// Wrapper to make a raw_ostream look like an output iterator. +/// Designed to match the API of std::ostream_iterator. +/// Enables reusing STL algorithms with raw_ostream. For example: +/// +/// \code{.cpp} +/// +/// std::vector V = { 1, 2, 3 }; +/// std::copy(V.begin(), V.end(), raw_ostream_iterator(outs(), ", ")); +/// +/// \endcode +/// +/// The code above outputs: "1, 2, 3, " +template class raw_ostream_iterator { + raw_ostream &OutStream; + const CharT *Delim; + +public: + using iterator_category = std::output_iterator_tag; + using value_type = void; + using difference_type = void; + using pointer = void; + using reference = void; + using char_type = CharT; + + /// Constructs the iterator with \p Stream as the associated stream and \p + /// Delim as the delimiter. + /// + /// \param Stream The output stream to be accessed by this iterator. + /// + /// \param Delim The null-terminated character string to be inserted into the + /// stream after each output. + raw_ostream_iterator(raw_ostream &Stream, const CharT *Delim) + : OutStream(Stream), Delim(Delim) {} + + /// Constructs the iterator with \p Stream as the associated stream and a null + /// pointer as the delimiter. + /// + /// \param Stream The output stream to be accessed by this iterator. + raw_ostream_iterator(raw_ostream &Stream) + : OutStream(Stream), Delim(nullptr) {} + + /// Inserts \p Value into the associated stream, then inserts the delimiter, + /// if one was specified at construction time. + /// + /// \param value The object to insert. + raw_ostream_iterator &operator=(const T &Value) { + OutStream << Value; + if (Delim != 0) + OutStream << Delim; + return *this; + } + + /// No-op. Provided to satisfy the requirements of LegacyOutputIterator. + ///@{ + raw_ostream_iterator &operator*() { return *this; } + raw_ostream_iterator &operator++() { return *this; } + raw_ostream_iterator &operator++(int) { return *this; } + ///@} +}; + } // end namespace llvm #endif // LLVM_SUPPORT_RAW_OSTREAM_H diff --git a/llvm/unittests/Support/raw_ostream_test.cpp b/llvm/unittests/Support/raw_ostream_test.cpp --- a/llvm/unittests/Support/raw_ostream_test.cpp +++ b/llvm/unittests/Support/raw_ostream_test.cpp @@ -377,6 +377,114 @@ { raw_fd_ostream("-", EC, sys::fs::OpenFlags::OF_None); } } +// Basic functionality tests for raw_ostream_iterator. +TEST(raw_ostream_iteratorTest, raw_ostream_iterator_basic) { + std::string S; + + // Expect no output if nothing is written to the iterator. + { + S = ""; + raw_string_ostream Str(S); + raw_ostream_iterator It(Str); + } + EXPECT_EQ(S, ""); + { + S = ""; + raw_string_ostream Str(S); + raw_ostream_iterator It(Str, ","); + } + EXPECT_EQ(S, ""); + + // Output one char. + { + S = ""; + raw_string_ostream Str(S); + raw_ostream_iterator It(Str); + *It = 'x'; + } + EXPECT_EQ(S, "x"); + { + S = ""; + raw_string_ostream Str(S); + raw_ostream_iterator It(Str, ","); + *It = 'x'; + } + EXPECT_EQ(S, "x,"); + + // Output one int. + { + S = ""; + raw_string_ostream Str(S); + raw_ostream_iterator It(Str); + *It = 5; + } + EXPECT_EQ(S, "5"); + { + S = ""; + raw_string_ostream Str(S); + raw_ostream_iterator It(Str, ","); + *It = 5; + } + EXPECT_EQ(S, "5,"); + + // Output a few ints. + { + S = ""; + raw_string_ostream Str(S); + raw_ostream_iterator It(Str); + *It = 5; + *It = 3; + *It = 2; + } + EXPECT_EQ(S, "532"); + { + S = ""; + raw_string_ostream Str(S); + raw_ostream_iterator It(Str, ","); + *It = 5; + *It = 3; + *It = 2; + } + EXPECT_EQ(S, "5,3,2,"); +} + +template +std::string iterator_str(InputIt First, InputIt Last) { + std::string S; + { + raw_string_ostream Str(S); + std::copy(First, Last, raw_ostream_iterator(Str)); + } + return S; +} + +template +std::string iterator_str(InputIt First, InputIt Last, const char *Delim) { + std::string S; + { + raw_string_ostream Str(S); + std::copy(First, Last, raw_ostream_iterator(Str, Delim)); + } + return S; +} + +// Test using raw_ostream_iterator as an output iterator with a std algorithm. +TEST(raw_ostream_iteratorTest, raw_ostream_iterator_std_copy) { + // Test the empty case. + std::vector Empty = {}; + EXPECT_EQ("", iterator_str(Empty.begin(), Empty.end())); + EXPECT_EQ("", iterator_str(Empty.begin(), Empty.end(), ", ")); + + // Test without a delimiter. + std::vector V1 = {'a', 'b', 'c'}; + EXPECT_EQ("abc", iterator_str(V1.begin(), V1.end())); + + // Test with a delimiter. + std::vector V2 = {1, 2, 3}; + EXPECT_EQ("1, 2, 3, ", iterator_str(V2.begin(), V2.end(), ", ")); +} + + TEST(raw_ostreamTest, flush_tied_to_stream_on_write) { std::string TiedToBuffer; raw_string_ostream TiedTo(TiedToBuffer);