How to use this manipulator
Asked Answered
D

2

6

This is a exercise for school, so please provide just hints and no complete examples ;-)

I have my own manipulator:

template<typename T, typename Tr=char_traits<T> >
ios_base& toggle(basic_ios<T,Tr>& io)
{
    if(io.flags() & ios::scientific)
    { io.unsetf(ios::scientific); io.flags(ios::fixed); }
    else { io.unsetf(ios::fixed); io.flags(ios::scientific); }
    return io;
 }

I wrote this, because I have to write a manipulator with the form ios_base& my_manip(basic_ios&).

If I use it like this (without using return value):

toggle(cout);

... that works fine. But if I use it like that:

toggle(cout) << 54444.6456555 << endl;

That does not work (Because std::ios_base does not have operator<<() as stated below).

In general I do not get what ios_base& my_manip(basic_ios&) could be useful for... Do you have a hint/example?


You guys already helped me a lot! What I still do NOT understand, is the motivation to pass a basic_ios and give back ios_base (because that is suggested to do in the exercise I have to solve...). What could be a possible scenario to use this???

Dickie answered 30/12, 2013 at 15:40 Comment(9)
Hint: Take a look at the overloads of operator<< for std::ostreams: en.cppreference.com/w/cpp/io/basic_ostream/operator_ltltTrexler
Hint2: An ios_base has no operator>> nor operator<< defined. That's why toggle(cout) << "something" doesn't work.Trexler
+1 This is a refreshing example of how to ask a homework question.Harbinger
Hint 3 (spoiler alert ;) Take a look at how endl is definedTrexler
@ DyP: Sorry, but your spoiler alert hint does not help me much :-( The point is, that I really should define the function as ios_base& my_manip(basic_ios&) (even though I do not get what it could be useful for...)Dickie
@Dickie The point is that std::ostream has several operator<< overloads that take function pointers and calls them. Using manipulators like std::endl, std::hex or your own is typically done by calling the operator<< (or operator>> for istreams) of the stream and passing a pointer to the manipulator function. Note that function names decay to pointers to those functions.Trexler
by the way: is there a difference between std::basic_ostream and std::ostream? If not, why are there two names?Dickie
@Dickie std::ostream is a typedef for std::basic_ostream<char>. There's also std::wostream, a typedef for std::basic_ostream<wchar_t>.Trexler
"What I still do NOT understand, is the motivation to pass a basic_ios and give back ios_base" Nobody spotted that yet :D -- It's an error if the manipulator is intended to be used as stream<<toggle. It has to be either basic_ios<T,Tr>& my_manip(basic_ios<T,Tr>&) or ios_base& my_manip(ios_base&). In your case, I think the base class ios_base is sufficient, it already provides the necessary functionality and is therefore the more general choice (see this class diagram). When using ios_base, you don't need a function template.Trexler
E
4

The problem with the manipulator is that it returns an std::ios_base& rather than a std::ostream& you can write to. You could change the manipulator to take an std::ostream& as parameter and return the reference received. However, the output stream class defines output operators which take pointers to functions:

std::ostream& std::ostream::operator<< (std::ios_base& (*)(std::ios_base&)) { ... }

That is, you can just insert manipulators pretty much the way you would do it with, e.g., std::hex:

std::cout << std::hex << 123 << ' ' << std::dec << 123 << '\n';
Exotic answered 30/12, 2013 at 15:55 Comment(0)
A
2

In addition to the issue which Dietmar addressed: io.flags() & ios::scientific does not return a bool, and the conversion to bool probably doesn't do what you want. You need something along the lines of:

if ( (io.flags() & ios::floatfield) == ios::fixed ) {
    io.setf( ios::scientific, ios::floatfield );
} else if ( (io.flags() & ios::floatfield) == ios::scientific ) {
    io.setf( ios::fixed, ios::floatfield );
} else {
    //  Whatever you want to happen first time around...
}

Despite being part of a variable with a type named ...flags, floatfield is not a flag, but a field which can take on at least three values: fixed, scientific and its default value in which neither of these are set. (basefield and adjustfield behave similarly.)

Note too the use of the two argument form of ios::setf; it is designed especially for these bitfield format parameters, and resets the bits in its second argument before setting the ones in its first.

I might add that you probably do not want to call io.flags in your manipulator; this sets all of the formatting flags to the value you give, effectively resetting all other formatting flags. If you're only outputting floating point, this may not be a problem (although showpos, showpoint, uppercase and possibly unitbuf might be relevant), but you never know.

Aftermost answered 30/12, 2013 at 16:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.