我正在做一个学校项目,我需要经常改变文字颜色。 项目目标是当前仅用于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";
我想要的是像setw
, iomainp.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-7operator<<(basic_ostream&, char)
和operator<<(basic_ostream&, char*)
operator<<(basic_ostream&, basic_string&)
std::put_money
(在money_put::put())
std::quoted
(当与输出流一起使用时)