"Permanent" Std::Setw

Permanent std::setw

Well, it's not possible. No way to make it call .width each time again. But you can use boost, of course:

#include <boost/function_output_iterator.hpp>
#include <boost/lambda/lambda.hpp>
#include <algorithm>
#include <iostream>
#include <iomanip>

int main() {
using namespace boost::lambda;
int a[] = { 1, 2, 3, 4 };
std::copy(a, a + 4,
boost::make_function_output_iterator(
var(std::cout) << std::setw(3) << _1)
);
}

It does create its own functor, but it happens behind the scene :)

What's the deal with setw()?

The way i see it is : You can always do something like below if you want it to be applied uniformly.

int width =2;
while(whatever)
{
mystream << std::setw(width) << myval;
}

but if it was sticky as you mention:

mystream.width(2);
while(whatever)
{
mystream << myval;
}

and if i wanted a different width every line I have to keep setting width.

So essentially both approaches are almost the same, and i would like or dislike them depending on what i am doing now.

C++ can setw and setfill pad the end of a string?

You can use manipulators std::left, std::right, and std::internal to choose where the fill characters go.

For your specific case, something like this could do:

#include <iostream>
#include <iomanip>
#include <string>

const char* C_TEXT = "Constant text ";
const size_t MAXWIDTH = 10;

void print(const std::string& var_text, int num)
{
std::cout << C_TEXT
// align output to left, fill goes to right
<< std::left << std::setw(MAXWIDTH) << std::setfill('.')
<< var_text << ": " << num << '\n';
}

int main()
{
print("1234567890", 42);
print("12345", 101);
}

Output:

Constant text 1234567890: 42
Constant text 12345.....: 101

EDIT:
As mentioned in the link, std::internal works only with integer, floating point and monetary output. For example with negative integers, it'll insert fill characters between negative sign and left-most digit.

This:

int32_t i = -1;
std::cout << std::internal
<< std::setfill('0')
<< std::setw(11) // max 10 digits + negative sign
<< i << '\n';
i = -123;
std::cout << std::internal
<< std::setfill('0')
<< std::setw(11)
<< i;

will output

-0000000001
-0000000123

Which iomanip manipulators are 'sticky'?

Important notes from the comments below:

By Martin:

@Chareles: Then by this requirement all manipulators are sticky. Except setw which seems to be reset after use.

By Charles:

Exactly! and the only reason that setw appears to behave differently is because there are requirements on formatted output operations to explicitly .width(0) the output stream.

The following is the discussion that lead to the above conclusion:


Looking at the code the following manipulators return an object rather than a stream:

setiosflags
resetiosflags
setbase
setfill
setprecision
setw

This is a common technique to apply an operation to only the next object that is applied to the stream. Unfortunately this does not preclude them from being sticky. Tests indicate that all of them except setw are sticky.

setiosflags:  Sticky
resetiosflags:Sticky
setbase: Sticky
setfill: Sticky
setprecision: Sticky

All the other manipulators return a stream object. Thus any state information they change must be recorded in the stream object and is thus permanent (until another manipulator changes the state). Thus the following manipulators must be Sticky manipulators.

[no]boolalpha
[no]showbase
[no]showpoint
[no]showpos
[no]skipws
[no]unitbuf
[no]uppercase

dec/ hex/ oct

fixed/ scientific

internal/ left/ right

These manipulators actually perform an operation on the stream itself rather than the stream object (Though technically the stream is part of the stream objects state). But I do not believe they affect any other part of the stream objects state.

ws/ endl/ ends/ flush

The conclusion is that setw seems to be the only manipulator on my version that is not sticky.

For Charles a simple trick to affect only the next item in the chain:

Here is an Example how an object can be used to temporaily change the state then put it back by the use of an object:

#include <iostream>
#include <iomanip>

// Private object constructed by the format object PutSquareBracket
struct SquareBracktAroundNextItem
{
SquareBracktAroundNextItem(std::ostream& str)
:m_str(str)
{}
std::ostream& m_str;
};

// New Format Object
struct PutSquareBracket
{};

// Format object passed to stream.
// All it does is return an object that can maintain state away from the
// stream object (so that it is not STICKY)
SquareBracktAroundNextItem operator<<(std::ostream& str,PutSquareBracket const& data)
{
return SquareBracktAroundNextItem(str);
}

// The Non Sticky formatting.
// Here we temporariy set formating to fixed with a precision of 10.
// After the next value is printed we return the stream to the original state
// Then return the stream for normal processing.
template<typename T>
std::ostream& operator<<(SquareBracktAroundNextItem const& bracket,T const& data)
{
std::ios_base::fmtflags flags = bracket.m_str.flags();
std::streamsize currentPrecision = bracket.m_str.precision();

bracket.m_str << '[' << std::fixed << std::setprecision(10) << data << std::setprecision(currentPrecision) << ']';

bracket.m_str.flags(flags);

return bracket.m_str;
}

int main()
{

std::cout << 5.34 << "\n" // Before
<< PutSquareBracket() << 5.34 << "\n" // Temp change settings.
<< 5.34 << "\n"; // After
}

> ./a.out
5.34
[5.3400000000]
5.34

Output formatting using setw and setfill

std::setw is a great tool for printing data with varying length in a table format. You specify the width of a column and spaces will get filled in automatically until the specified column width is reached. However, if you need a fixed number of spaces it's easier to hard code them: cout << " ". If you need the same indentation many times you might want to define this as a constant

auto indent = string(3, ' ');
cout << indent << ...;

This allows you to easily adjust the indentation later if needed.

Effective use of C++ iomanip library

Only std::setw() is temporary. The other two calls, setiosflags, and setprecision have a permanent effect.

So, you could change your code to :

std::ostream& operator<<(std::ostream &output, const Vector &v){
output<<"["
<<std::setiosflags(std::ios::right | std::ios::scientific)
<<std::setw(23)
<<std::setprecision(16)
<<v._x<<", "
<<std::setw(23)
<<v._y<<", "
<<std::setw(23)
<<v._z<<"]";
return output;
}

But now you've borked the flags and precision for the next guy. Try this instead:

std::ostream& operator<<(std::ostream &output, const Vector &v){
std::ios_base::fmtflags f = output.flags(std::ios::right | std::ios::scientific);
std::streamsize p = output.precision(16);
output<<"["
<<std::setw(23)
<<v._x<<", "
<<std::setw(23)
<<v._y<<", "
<<std::setw(23)
<<v._z<<"]";
output.flags(f);
output.precision(p);
return output;
}

Finally, if you absolutely have to get rid of the duplication of the constant 23, you could do something like this (but I wouldn't recommend it):

struct width {
int w;
width(int w) : w(w) {}
friend std::ostream& operator<<(std::ostream&os, const width& w) {
return os << std::setw(width.w);
}
};

std::ostream& operator<<(std::ostream &output, const Vector &v){
std::ios_base::fmtflags f = output.flags(std::ios::right | std::ios::scientific);
std::streamsize p = output.precision(16);
width w(23);
output<<"["
<<w
<<v._x<<", "
<<w
<<v._y<<", "
<<w
<<v._z<<"]";
output.flags(f);
output.precision(p);
return output;
}

See also this other question, where they decided that you can't make width permanent.



Related Topics



Leave a reply



Submit