How can I make my function sticky for overloaded iostream extraction operators

I am engaged in a school project in which I often need to change the color of the text. The goal of the project is currently a console application for Windows only. Using Codeblocks with MinGW for debugging. I'm not a noob, but intermediate-ish.

Therefore, using this everywhere in the code was ugly:

SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), __col._colour_code); 

Even if I wrapped it in a function, it is still cumbersome and ugly because you cannot continue your cout chain. You split the chain like you should call SetColour in the new expression, for example:

 SetColour(GRAY); cout << setcol(PURPLE) << " ID:["; SetColour(AQUA); cout << song.GetID(); SetColour(GRAY); cout << "]" << " "; SetColour(GREEN); cout << song.GetTitle(); SetColour(WHITE); cout << " by "; SetColour(BRIGHT); cout << song.GetArtist() << "\n"; 

What I wanted was functionality like setw , setprecision , etc. So I opened iomainp.h and looked for some hints:

 struct _Setw { int _M_n; }; inline _Setw setw(int __n) { return { __n }; } template<typename _CharT, typename _Traits> inline basic_istream<_CharT, _Traits>& operator>>(basic_istream<_CharT, _Traits>& __is, _Setw __f) { __is.width(__f._M_n); return __is; } template<typename _CharT, typename _Traits> inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, _Setw __f) { __os.width(__f._M_n); return __os; } 

Thus, I created a new function myself in a 100% way:

 enum Colour { BLACK=0x00, DARK_GREEN=0x02, WHITE=0x07, GRAY, BLUE=0x09, GREEN, AQUA, RED, PURPLE, YELLOW, BRIGHT }; struct _colour_struct { uint8_t _colour_code; }; inline _colour_struct setcolour (Colour colour_foregrnd, Colour colour_backgrnd =BLACK) { uint8_t colour_code = colour_backgrnd; colour_code <<= 4; colour_code |= colour_foregrnd; return { colour_code }; } namespace std { template<typename _CharT, typename _Traits> inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, _colour_struct __col) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), __col._colour_code); return __os; } } 

SURPRISE! (for me) His work !! eg:.

 cout << setcolour(GRAY) << " ID:[" << setcolour(AQUA) << song.GetID() << setcolour(GRAY) << "]" << " " << setcolour(GREEN) << song.GetTitle() << setcolour(WHITE) << " by "<< setcolour(BRIGHT) << song.GetArtist() << "\n"; 

But consider the output of this code:

 std::cout << std::setw(20) << setcolour(AQUA) << "1st time" << "\n"; std::cout << "2nd time" << "\n"; std::cout << "3rd time" << "\n"; 

Note that setw DID NOT stick, this was a Reset in the second line. How?? (Debugging did not show that an additional call was made before Reset.)

But my SetColour DID is for the rest of the program. What for?? (Although its 100% similar to setw ).

How to make SetColour same as setw ??? I need this function to make my program cleaner and more logical.

I also found the following: Which iomanip manipulators are sticky

But the answers and comments only confused me. Apparently setw calls cout.width (0), but debugging did not show such a call, and such a line of code was not found in iomanip.h . And did not understand the answer there. Please explain.

EDIT

Perhaps I was not a direct question in the question. How cout.width(0) (in the context of setw ) is called every time,

How can I make my SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0) (in the context of SetColour ) called every time? How do I approach this problem?

+5
source share
2 answers

Width is handled specifically: all built-in operators will reset to width after the output of the object. The fact that you do not see the corresponding width(0) call in the code or debugger does not mean that it does not exist! It can be built in and not hit a breakpoint, for example. As for the code that you will need to look for, for example, in the implementation of std::num_put<...> : the actual output statement does not include the code, but the do_put() functions are executed.

To reset other formatting flags you will need to connect to some operation called after each output operation. However, there are not many possibilities: threads do not support general configuration after each object. The following are things you can do, but I would recommend leaving the formatting flags sticky. Probably the mistake is to make a special width:

  • Digital formatting is done using the do_put() virtual functions in std::num_put<cT> . These functions can be overridden and, for example, perform normal formatting, delegating the implementation of the base class, and then something else.

    The key problem with this approach in your setup is that it does not work with non-numeric output. For example, line output will not be reset by anything.

  • You can set the formatting flag std::unitbuf , which causes a reset after each [correctly written] output statement. The flash is converted to a pubsync() call and ultimately sync() in the std::basic_streambuf<cT> streams. The sync() function can be overridden. That is, the approach will set the user buffer stream and the std::ios_base::unitbuf when setting sone flags, which sends output to the original stream and when the sync() call sync() flags.

    Besides the fact that you have a problem, you cannot distinguish a genuine flash from an automatic reset (the main purpose of srd::ios_base::unitbuf is to reset std::cerr ). In addition, reset occurs when the first std::ostream::sentry . For compound values ​​that are most likely after formatting the first part.

  • In any case, the color formatter uses a temporary object. The destructor of this object can be used to reset some fornatting flags. The output operator set the necessary information about the stream to the appropriate object when it receives a "formatted" one (possibly using the mutable member). Of course, this means that the format and output must be set from the same operator. In addition, all output from the same operator gets the same format.

I don't know any other formatting approach after some output. None of the approaches work particularly well. I would rather use a protective approach to set / unset flags than try to be smart.

+1
source

Note that setw DID NOT stick, this was a Reset in the second line. How?

When you use cout << setcolour(AQUA) , you are constantly changing the state of your program, which will be used later.

This does not apply to std::setw() . std::setw() sets the width of the next output and then resets the width to zero. See http://en.cppreference.com/w/cpp/io/manip/setw for more details. In particular, Notes :

The width property of the stream will be reset to zero (which means "unspecified") if any of the following functions is called:

  • Input
    • operator>>(basic_istream&, basic_string&)
    • operator>>(basic_ostream&, char*)
  • Output
    • Overloads 1-7 from basic_ostream::operator<<() (in step 3 of num_put::put() )
    • operator<<(basic_ostream&, char) and operator<<(basic_ostream&, char*)
    • operator<<(basic_ostream&, basic_string&)
    • std::put_money (inside money_put::put())
    • std::quoted (when used with the output stream)
0
source

Source: https://habr.com/ru/post/1235516/


All Articles