我如何使我的function粘性重载iostream提取操作符

我正在做一个学校项目,我需要经常改变文字颜色。 项目目标是当前仅用于Windows控制台应用程序。 使用MinGW进行debugging的代码块。 我不是一个noob,但中级ISH。

所以在代码中使用这个地方是丑陋的:

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

即使我把它包装在一个函数中,它仍然是笨重和丑陋的,因为你不能继续你的cout链。 您必须打破链条,因为您必须在新的语句中调用SetColour ,例如:

 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"; 

我想要的是像setwiomainp.h等function。所以我打开iomainp.h并寻找一些提示:

 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; } 

所以我用100%的模拟方式创build了我自己的新function:

 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; } } 

惊喜! (对我来说)它的工作! 例如:

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

但考虑这个代码的输出:

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

请注意, setw DID不粘,它在第二行是Reset。 怎么样 ?? (debugging显示没有额外的呼叫被执行来重置它。)

但是我的setcolour DID坚持剩下的程序。 为什么? (虽然它100%类似于setw )。

我怎样才能使setcolour就像setw ??? 我需要这个function来使我的程序更加干净和合乎逻辑。

我还发现这个: iomanip操纵器是粘性的

但只是在那里的答案和评论只是困惑了我。 显然, setw调用cout.width(0),但debugging显示没有这样的调用,在iomanip.h也没有find这样的代码行。 那里也没有明白答案。 请解释。

编辑

也许我不直接问这个问题。 像cout.width(0) (在上下文中)每次被调用,

我怎样才能使SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0) (在setcolour上下文中)每次被调用? 我应该如何解决这个问题?

宽度是专门处理的:所有的内置操作符将在输出对象后重新设置宽度。 在代码或调试器中看不到相应的调用width(0)的事实并不意味着它不存在! 例如,可以内联而不是打断点。 关于代码你需要看看,例如,在执行std::num_put<...> :实际的输出操作符不包括代码,但do_put()函数。

要重置其他格式标志,您需要挂钩到每个输出操作后调用的某个操作。 但是没有多少机会:在每个对象之后,流不支持通用定制。 下面是可以做的事情,但我会建议离开格式化标志粘性。 使宽度变得特别是错误的:

  • 数字格式化通过std::num_put<cT>do_put()虚函数完成。 这些functiins可以被覆盖,例如,通过委托给基类实现,然后是别的东西,来进行正常的格式化。

    这种方法在您的设置中的关键问题是,它不适用于非数字输出。 例如,输出字符串不会重置任何东西。

  • 在每个[正确写入]输出操作符之后,可以设置格式化标志std::unitbuf导致刷新。 flush被转换为对std::basic_streambuf<cT>流的pubsync()和最终sync()的调用。 sync()函数可以被覆盖。 也就是说,该方法将安装一个自定义的流缓冲区和标志std::ios_base::unitbuf时设置sone标志发送输出到原始流和调用sync()重置标志。

    除了有点人为的问题,它有问题,你不能区分一个真正的刷新自动刷新( srd::ios_base::unitbuf的主要目的是得到std::cerr刷新)。 另外,重置发生在第一个被摧毁的std::ostream::sentry上。 对于格式化第一部分之后最有可能的复合值。

  • 颜色格式化程序无论如何都使用临时对象。 这个对象的dstructor可以用来重置一些fornatting标志。 当输出操作符被“格式化”(可能使用mutable成员)时,输出操作符将在相应的对象上设置必要的流信息。 当然,这意味着设置格式和输出需要从同一个语句中完成。 而且,同一个语句中的所有输出都接收相同的格式。

我不知道任何其他的方法来处理格式输出后。 没有一种方法特别适合。 我宁愿使用类似警卫的方法设置/取消设置标志,而不是试图变得聪明。

请注意,setw DID不粘,它在第二行是Reset。 怎么样 ??

当你使用cout << setcolour(AQUA) ,你正在对你以后使用的程序的状态进行持久的改变。

std::setw()不是这种情况。 std::setw()只设置下一个输出的宽度,然后将宽度重置为零。 有关更多详细信息,请参见http://en.cppreference.com/w/cpp/io/manip/setw 。 特别是,

如果调用以下任何函数,则流的width属性将重置为零(表示“未指定”):

  • 输入
    • operator>>(basic_istream&, basic_string&)
    • operator>>(basic_ostream&, char*)
  • 产量
    • 重载basic_ostream::operator<<() (在num_put::put()第3阶段basic_ostream::operator<<() 1-7
    • operator<<(basic_ostream&, char)operator<<(basic_ostream&, char*)
    • operator<<(basic_ostream&, basic_string&)
    • std::put_money (在money_put::put())
    • std::quoted (当与输出流一起使用时)