Skip to content

Commit 7479a2e

Browse files
committed
[Support] Add raw_ostream_iterator: ostream_iterator for raw_ostream
Adds a class `raw_ostream_iterator` that behaves like std::ostream_iterator, but can be used with raw_ostream. This is useful for using raw_ostream with std algorithms. For example, it can be used to output std containers as follows: ``` std::vector<int> V = { 1, 2, 3 }; std::copy(V.begin(), V.end(), raw_ostream_iterator<int>(outs(), ", ")); // Output: "1, 2, 3, " ``` The API tries to follow std::ostream_iterator as closely as is practically possible. Reviewed By: dblaikie, mkitzan Differential Revision: https://reviews.llvm.org/D78795
1 parent d268feb commit 7479a2e

File tree

2 files changed

+169
-0
lines changed

2 files changed

+169
-0
lines changed

llvm/include/llvm/Support/raw_ostream.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <cstddef>
2222
#include <cstdint>
2323
#include <cstring>
24+
#include <iterator>
2425
#include <string>
2526
#include <system_error>
2627
#include <type_traits>
@@ -714,6 +715,66 @@ class buffer_unique_ostream : public raw_svector_ostream {
714715
~buffer_unique_ostream() override { *OS << str(); }
715716
};
716717

718+
/// Wrapper to make a raw_ostream look like an output iterator.
719+
/// Designed to match the API of std::ostream_iterator.
720+
/// Enables reusing STL algorithms with raw_ostream. For example:
721+
///
722+
/// \code{.cpp}
723+
///
724+
/// std::vector<int> V = { 1, 2, 3 };
725+
/// std::copy(V.begin(), V.end(), raw_ostream_iterator<int>(outs(), ", "));
726+
///
727+
/// \endcode
728+
///
729+
/// The code above outputs: "1, 2, 3, "
730+
template <class T, class CharT = char> class raw_ostream_iterator {
731+
raw_ostream &OutStream;
732+
const CharT *Delim;
733+
734+
public:
735+
using iterator_category = std::output_iterator_tag;
736+
using value_type = void;
737+
using difference_type = void;
738+
using pointer = void;
739+
using reference = void;
740+
using char_type = CharT;
741+
742+
/// Constructs the iterator with \p Stream as the associated stream and \p
743+
/// Delim as the delimiter.
744+
///
745+
/// \param Stream The output stream to be accessed by this iterator.
746+
///
747+
/// \param Delim The null-terminated character string to be inserted into the
748+
/// stream after each output.
749+
raw_ostream_iterator(raw_ostream &Stream, const CharT *Delim)
750+
: OutStream(Stream), Delim(Delim) {}
751+
752+
/// Constructs the iterator with \p Stream as the associated stream and a null
753+
/// pointer as the delimiter.
754+
///
755+
/// \param Stream The output stream to be accessed by this iterator.
756+
raw_ostream_iterator(raw_ostream &Stream)
757+
: OutStream(Stream), Delim(nullptr) {}
758+
759+
/// Inserts \p Value into the associated stream, then inserts the delimiter,
760+
/// if one was specified at construction time.
761+
///
762+
/// \param value The object to insert.
763+
raw_ostream_iterator &operator=(const T &Value) {
764+
OutStream << Value;
765+
if (Delim != 0)
766+
OutStream << Delim;
767+
return *this;
768+
}
769+
770+
/// No-op. Provided to satisfy the requirements of LegacyOutputIterator.
771+
///@{
772+
raw_ostream_iterator &operator*() { return *this; }
773+
raw_ostream_iterator &operator++() { return *this; }
774+
raw_ostream_iterator &operator++(int) { return *this; }
775+
///@}
776+
};
777+
717778
} // end namespace llvm
718779

719780
#endif // LLVM_SUPPORT_RAW_OSTREAM_H

llvm/unittests/Support/raw_ostream_test.cpp

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,114 @@ TEST(raw_fd_ostreamTest, multiple_raw_fd_ostream_to_stdout) {
377377
{ raw_fd_ostream("-", EC, sys::fs::OpenFlags::OF_None); }
378378
}
379379

380+
// Basic functionality tests for raw_ostream_iterator.
381+
TEST(raw_ostream_iteratorTest, raw_ostream_iterator_basic) {
382+
std::string S;
383+
384+
// Expect no output if nothing is written to the iterator.
385+
{
386+
S = "";
387+
raw_string_ostream Str(S);
388+
raw_ostream_iterator<char> It(Str);
389+
}
390+
EXPECT_EQ(S, "");
391+
{
392+
S = "";
393+
raw_string_ostream Str(S);
394+
raw_ostream_iterator<char> It(Str, ",");
395+
}
396+
EXPECT_EQ(S, "");
397+
398+
// Output one char.
399+
{
400+
S = "";
401+
raw_string_ostream Str(S);
402+
raw_ostream_iterator<char> It(Str);
403+
*It = 'x';
404+
}
405+
EXPECT_EQ(S, "x");
406+
{
407+
S = "";
408+
raw_string_ostream Str(S);
409+
raw_ostream_iterator<char> It(Str, ",");
410+
*It = 'x';
411+
}
412+
EXPECT_EQ(S, "x,");
413+
414+
// Output one int.
415+
{
416+
S = "";
417+
raw_string_ostream Str(S);
418+
raw_ostream_iterator<int> It(Str);
419+
*It = 5;
420+
}
421+
EXPECT_EQ(S, "5");
422+
{
423+
S = "";
424+
raw_string_ostream Str(S);
425+
raw_ostream_iterator<int> It(Str, ",");
426+
*It = 5;
427+
}
428+
EXPECT_EQ(S, "5,");
429+
430+
// Output a few ints.
431+
{
432+
S = "";
433+
raw_string_ostream Str(S);
434+
raw_ostream_iterator<int> It(Str);
435+
*It = 5;
436+
*It = 3;
437+
*It = 2;
438+
}
439+
EXPECT_EQ(S, "532");
440+
{
441+
S = "";
442+
raw_string_ostream Str(S);
443+
raw_ostream_iterator<int> It(Str, ",");
444+
*It = 5;
445+
*It = 3;
446+
*It = 2;
447+
}
448+
EXPECT_EQ(S, "5,3,2,");
449+
}
450+
451+
template <class T, class InputIt>
452+
std::string iterator_str(InputIt First, InputIt Last) {
453+
std::string S;
454+
{
455+
raw_string_ostream Str(S);
456+
std::copy(First, Last, raw_ostream_iterator<T>(Str));
457+
}
458+
return S;
459+
}
460+
461+
template <class T, class InputIt>
462+
std::string iterator_str(InputIt First, InputIt Last, const char *Delim) {
463+
std::string S;
464+
{
465+
raw_string_ostream Str(S);
466+
std::copy(First, Last, raw_ostream_iterator<T>(Str, Delim));
467+
}
468+
return S;
469+
}
470+
471+
// Test using raw_ostream_iterator as an output iterator with a std algorithm.
472+
TEST(raw_ostream_iteratorTest, raw_ostream_iterator_std_copy) {
473+
// Test the empty case.
474+
std::vector<char> Empty = {};
475+
EXPECT_EQ("", iterator_str<char>(Empty.begin(), Empty.end()));
476+
EXPECT_EQ("", iterator_str<char>(Empty.begin(), Empty.end(), ", "));
477+
478+
// Test without a delimiter.
479+
std::vector<char> V1 = {'a', 'b', 'c'};
480+
EXPECT_EQ("abc", iterator_str<char>(V1.begin(), V1.end()));
481+
482+
// Test with a delimiter.
483+
std::vector<int> V2 = {1, 2, 3};
484+
EXPECT_EQ("1, 2, 3, ", iterator_str<int>(V2.begin(), V2.end(), ", "));
485+
}
486+
487+
380488
TEST(raw_ostreamTest, flush_tied_to_stream_on_write) {
381489
std::string TiedToBuffer;
382490
raw_string_ostream TiedTo(TiedToBuffer);

0 commit comments

Comments
 (0)