Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ custom stream manipulator that changes next item on stream

Tags:

c++

stream

In C++, to print a number in hexadecimal you do this:

int num = 10; std::cout << std::hex << num; // => 'a' 

I know I can create a manipulator that just adds stuff to the stream like so:

std::ostream& windows_feed(std::ostream& out) {     out << "\r\n";     return out; }  std::cout << "Hello" << windows_feed; // => "Hello\r\n" 

However, how can I create a manipulator that, like 'hex', modifies items to come on the stream? As a simple example, how would I create the plusone manipulator here?:

int num2 = 1; std::cout << "1 + 1 = " << plusone << num2; // => "1 + 1 = 2"  // note that the value stored in num2 does not change, just its display above. std::cout << num2; // => "1" 
like image 302
Tristan Havelick Avatar asked Apr 28 '09 20:04

Tristan Havelick


People also ask

What is a parameterized stream manipulator?

A parameterized manipulator is implemented in two parts: The manipulator. It takes an extra parameter. In the previous code example, it takes an extra int parameter. You cannot place this manipulator function in a sequence of input or output operations, since there is no shift operator defined for it.

What do you mean by manipulators in C++?

Manipulators are used to changing formatting parameters on streams and to insert or extract certain special characters.

Which stream manipulator can be used to display Boolean values as strings?

Line 18 uses manipulator boolalpha to display the bool value as a string.


2 Answers

First, you have to store some state into each stream. You can do that with the function iword and an index you pass to it, given by xalloc:

inline int geti() {      static int i = ios_base::xalloc();     return i; }  ostream& add_one(ostream& os) { os.iword(geti()) = 1; return os; }  ostream& add_none(ostream& os) { os.iword(geti()) = 0; return os; } 

Having that in place, you can already retrieve some state in all streams. Now, you just have to hook into the respective output operation. Numeric output is done by a facet, because it potentially is locale dependent. So you can do

struct my_num_put : num_put<char> {     iter_type      do_put(iter_type s, ios_base& f, char_type fill, long v) const {          return num_put<char>::do_put(s, f, fill, v + f.iword(geti()));      }       iter_type      do_put(iter_type s, ios_base& f, char_type fill, unsigned long v) const {          return num_put<char>::do_put(s, f, fill, v + f.iword(geti()));      }  };  

Now, you can test the stuff.

int main() {     // outputs: 11121011     cout.imbue(locale(locale(),new my_num_put));     cout << add_one << 10 << 11           << add_none << 10 << 11; } 

If you want that only the next number is incremented, just set the word to 0 again after each call to do_put.

like image 173
Johannes Schaub - litb Avatar answered Oct 11 '22 17:10

Johannes Schaub - litb


I totally agree with Neil Butterworth on this one, however in the specific case you are using you could do this totally horrible hack. Do not do this in any production code. It has lots of bugs. For one thing it only works in your one-liner above, it does not change the state of the underlying stream.

class plusone_stream : public std::ostream {   public:     std::ostream operator<<(int i)     {       _out << i+1;       return *this;     } };  std::ostream& plusone(std::ostream& out) {     return plusone_stream(out); } 
like image 22
1800 INFORMATION Avatar answered Oct 11 '22 17:10

1800 INFORMATION