3

I am formatting some log output. I want the end result to look like this:

Foo.................................12.1 Bar....................................4 Foo Bar............................42.01 

The total width is constant but both parameters (name and value) have various sizes. Is there a clean way to get the width of a given parameter, once included in a std::ostream?

#include <iostream> struct Entry { std::string name_; double value_; }; constexpr int line_width = 30; std::ostream& operator<<(std::ostream& log, const Entry& e) { log << e.name_ << std::string(line_width - e.name_.size(), '.') \\ should subtract the width of e.value_ << e.value_; return log; } int main() { Entry foo = { "Foo", 12.1 }; Entry bar = { "Bar", 4}; Entry foobar = { "Foo Bar", 42.01}; std::cout << foo << '\n' << bar << '\n' << foobar << '\n'; } 

The code above won't work because I did not subtract the width of the value. I was thinking about writing a function that would do something like this:

template <typename T> int get_width(std::ostream& log, T value) { // 1. use tellp to get initial stream size // 2. insert the value in the stream // 3. use tellp to get final stream size // 4. remove the value from the stream (is that even possible?) // 5. return the size = final - initial } 

Is there a clean way to reach my goal?

2
  • Is the maximum output length of name_ and value_ known ahead of time? Or does each maximum of those two widths need to be calculated from the data, and then use that to determine the layout? Commented Sep 25, 2019 at 11:33
  • Value is a double specified by the user. It can technically contain 15-ish digits, but in practice people use 3 or 4. Commented Sep 25, 2019 at 11:35

2 Answers 2

2

As posted, the question is a bit of an X-Y problem, since you don't need to know the width to achieve the output you want. For this, it should be enough to know the total width of the field, and use a suitable fill character.

This should work for you:

std::ostream& operator<<(std::ostream& log, const Entry& e) { auto beg = log.tellp(); log << e.name_; auto len = log.tellp() - beg; auto oldFill = log.fill(); auto oldWidth = log.width(); log.fill('.'); log.width(line_width - len); log << e.value_; log.fill(oldFill); log.width(oldWidth); return log; } 

[Live example]

Note that this relies on the stream actually being capable of reporting valid values by tellp(). File-based streams are; std::cout connected to a terminal is not.

Sign up to request clarification or add additional context in comments.

1 Comment

Although this code does the job, it really isn't very expressive/clean imho. If another developer takes a look at these few lines, he will have no idea what's going on. I can add comments, but I'd rather have a "cleaner" code in the first place. Is there a way to encapsulate some of that? This will only be called once in the code, so performance is not hugely important here.
1

You could create one string containing the "name", and one string containing the "value".

Then it's easy to calculate the total length of these two strings, and from that calculate the space needed between them.

5 Comments

If I do that, it won't follow the settings of my output stream though, right?
@Touloudou That really depends. You could use an std::ostringstream to create the string for the numbers, and use the standard I/O manipulators for that. Or did you mean something else? Can you pleas elaborate?
I have to add my lines to an existing output stream, which has a custom format (precision, etc.). I need to make sure that the newly created string is formatted in the exact same manner.
@Touloudou You could get the flags and options set for the output stream and use them for the string streams.
I think I'm gonna go with your initial idea. It's a logger, so I guess it does not really matter if the formatting is not exactly the same. Using the flags would make the functions a lot more complex for little benefits.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.