davidhigh
davidhigh

Reputation: 15518

C++ indent overloaded ostream operator

Let's say Ì have some class and added output functionality by overloading the left-shift operator:

struct Foo
{
    int i = 1;
    std::string s = "hello";
};

auto& operator<<(std::ostream& os, Foo const& foo)
{
     os<<foo.i<<"\n";
     os<<foo.s<<"\n";
     return os;
}

What is a good way to indent the output?

Example: If I write

std::cout<<"    "<<Foo{}<<std::endl;

the output is:

    1
hello

Obviously, hello is not indented. Is there an easy way to indent the whole output (and not just the first element)?

Upvotes: 3

Views: 1271

Answers (2)

Michael Gaskill
Michael Gaskill

Reputation: 8042

You can use the standard library manipulator setw to set the width of a field, which often results in indenting the text. Here's how you use it:

cout << std::setw(10) << "Viola!" << std::endl;

This will print the word "Viola!" indented by 4 spaces. Why 4 spaces? The parameter to setw() determines the entire width of the "field", which includes the 6 characters in "Viola!".

By default, setw() will align the text to the right, but can be made to align left by using another manipulator left. Here's an example:

cout << std::setw(10) << std::left << "Viola!" << std::endl;

This will output the string "Viola!" with no indentation, but with 4 spaces after it.

That should answer your original question about a good way to indent, and setw() is not just a good way, but the standard way.

The second question asks about how to have persistent indentation, and the answer is that there is not an easy way. The easiest approach is to add the call to setw() (or whichever indentation method that you use) in each of the calls to cout.

In addition to those answers, you should consider replacing the use of "\n" in your calls to cout with a call to endl. endl is the "end of line" manipulator, and makes your code work properly with any output stream. The code would look like this:

auto& operator<<(std::ostream& os, Foo const& foo)
{
     os << foo.i << std::endl;
     os << foo.s << std::endl;
     return os;
}

Upvotes: 0

You're serializing the Foo object right? So logically the serialized string of Foo is an implementation detail of Foo. You could write your own stream class or something along those lines but that is overengineering the problem.

auto& operator<<(std::ostream& os, Foo const& foo)
{
    auto s = "\t" + std::to_string(foo.i) + "\n"
             "\t" + foo.s;
    return (os << s);
}

int main()
{
    std::cout << Foo{} << "\n";
}

Upvotes: 1

Related Questions