如何使我的函数对重载的 iostream 提取运算符具有粘性
How can I make my function sticky for overloaded iostream extraction operators
我正在做一个学校项目,我需要经常更改其中的文本颜色。
项目目标是控制台应用程序,目前仅适用于 Windows。使用 Codeblocks 和 MinGW 进行调试。
我不是菜鸟,而是中级。
所以在代码中到处使用它是丑陋的:
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
、setprecision
等功能。所以我打开 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% 类似的方式创建了自己的新函数:
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
没有坚持,它在第二行被重置了。如何 ??
(调试显示没有执行额外的调用来重置它。)
但是我的 setcolour
DID 坚持了程序的其余部分。为什么 ??
(尽管它 100% 类似于 setw
)。
我怎样才能让 setcolour
和 setw
一样??? 我需要这个功能来让我的程序更干净和更有逻辑性。
我还发现了这个:
Which iomanip manipulators are sticky
但是那里的答案和评论只会让我感到困惑。显然,setw
调用了cout.width(0),但调试显示没有这样的调用,在iomanip.h
中也没有找到这样的一行代码。也不明白那里的答案。请解释。
编辑
也许我没有直接问这个问题。
就像每次调用 cout.width(0)
(在 setw
的上下文中)一样,
如何让我的 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0)
(在 setcolour
的上下文中)每次都被调用???
我该如何解决这个问题??
Notice that setw DIDN'T stick, it was Reset in the second line. How ??
当您使用 cout << setcolour(AQUA)
时,您正在对稍后使用的程序状态进行持久更改。
std::setw()
并非如此。 std::setw()
仅设置下一个输出的宽度,然后将宽度重置为零。有关详细信息,请参阅 http://en.cppreference.com/w/cpp/io/manip/setw。特别是 Notes:
The width property of the stream will be reset to zero (meaning "unspecified") if any of the following functions are called:
- Input
operator>>(basic_istream&, basic_string&)
operator>>(basic_ostream&, char*)
- Output
- Overloads 1-7 of
basic_ostream::operator<<()
(at Stage 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 an output stream)
宽度有特殊处理:所有内置算子输出一个对象后都会重新设置宽度。您在代码或调试器中没有看到对 width(0)
的相应调用这一事实并不意味着它不存在!例如,它可能被内联并且没有遇到断点。关于您需要查看的代码,例如,在 std::num_put<...>
的实现中:实际输出运算符不包含代码,但 do_put()
函数包含代码。
要重置其他格式化标志,您需要挂接到每个输出操作后调用的某些操作。不过机会不多:流不支持在每个对象之后进行通用定制。以下是 可以 完成的事情,但我建议保留格式化标志。将宽度设为特殊可以说是错误的:
数字格式是通过 std::num_put<cT>
中的 do_put()
虚函数完成的。这些函数可以被覆盖,例如,通过委托给基础 class 实现来进行正常格式化,然后是其他东西。
在您的设置中,此方法的关键问题是它不适用于非数字输出。例如,输出字符串不会重置任何内容。
可以设置格式化标志 std::unitbuf
在每个 [正确编写的] 输出运算符后导致刷新。刷新被转换为对 pubsync()
的调用,并最终在流 std::basic_streambuf<cT>
上调用 sync()
。 sync()
函数可以被覆盖。也就是说,该方法将在设置将输出发送到原始流的 sone 标志时安装自定义流缓冲区和标志 std::ios_base::unitbuf
,并在调用 sync()
时重置标志。
除了有点做作之外,它还有一个问题,就是您无法区分真正的冲洗和自动冲洗(srd::ios_base::unitbuf
的主要目的是让 std::cerr
冲洗)。此外,重置发生在第一个 std::ostream::sentry
被销毁时。对于最有可能在格式化第一部分之后的复合值。
无论如何,颜色格式化程序使用临时对象。该对象的构造函数可用于重置一些格式化标志。输出运算符在获得 "formatted" 时会在相应的对象上设置必要的流信息(可能使用 mutable
成员)。当然,这意味着设置格式和输出需要在同一条语句中完成。此外,同一语句的所有输出都采用相同的格式。
我不知道在某些输出后处理格式化的任何其他方法。 None 的方法效果特别好。我宁愿使用类似警卫的方法来处理 set/unset 标志,而不是试图变得聪明。
我正在做一个学校项目,我需要经常更改其中的文本颜色。 项目目标是控制台应用程序,目前仅适用于 Windows。使用 Codeblocks 和 MinGW 进行调试。 我不是菜鸟,而是中级。
所以在代码中到处使用它是丑陋的:
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
、setprecision
等功能。所以我打开 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% 类似的方式创建了自己的新函数:
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
没有坚持,它在第二行被重置了。如何 ??
(调试显示没有执行额外的调用来重置它。)
但是我的 setcolour
DID 坚持了程序的其余部分。为什么 ??
(尽管它 100% 类似于 setw
)。
我怎样才能让 setcolour
和 setw
一样??? 我需要这个功能来让我的程序更干净和更有逻辑性。
我还发现了这个: Which iomanip manipulators are sticky
但是那里的答案和评论只会让我感到困惑。显然,setw
调用了cout.width(0),但调试显示没有这样的调用,在iomanip.h
中也没有找到这样的一行代码。也不明白那里的答案。请解释。
编辑
也许我没有直接问这个问题。
就像每次调用 cout.width(0)
(在 setw
的上下文中)一样,
如何让我的 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0)
(在 setcolour
的上下文中)每次都被调用???
我该如何解决这个问题??
Notice that setw DIDN'T stick, it was Reset in the second line. How ??
当您使用 cout << setcolour(AQUA)
时,您正在对稍后使用的程序状态进行持久更改。
std::setw()
并非如此。 std::setw()
仅设置下一个输出的宽度,然后将宽度重置为零。有关详细信息,请参阅 http://en.cppreference.com/w/cpp/io/manip/setw。特别是 Notes:
The width property of the stream will be reset to zero (meaning "unspecified") if any of the following functions are called:
- Input
operator>>(basic_istream&, basic_string&)
operator>>(basic_ostream&, char*)
- Output
- Overloads 1-7 of
basic_ostream::operator<<()
(at Stage 3 ofnum_put::put()
)operator<<(basic_ostream&, char)
andoperator<<(basic_ostream&, char*)
operator<<(basic_ostream&, basic_string&)
std::put_money
(insidemoney_put::put())
std::quoted
(when used with an output stream)
宽度有特殊处理:所有内置算子输出一个对象后都会重新设置宽度。您在代码或调试器中没有看到对 width(0)
的相应调用这一事实并不意味着它不存在!例如,它可能被内联并且没有遇到断点。关于您需要查看的代码,例如,在 std::num_put<...>
的实现中:实际输出运算符不包含代码,但 do_put()
函数包含代码。
要重置其他格式化标志,您需要挂接到每个输出操作后调用的某些操作。不过机会不多:流不支持在每个对象之后进行通用定制。以下是 可以 完成的事情,但我建议保留格式化标志。将宽度设为特殊可以说是错误的:
数字格式是通过
std::num_put<cT>
中的do_put()
虚函数完成的。这些函数可以被覆盖,例如,通过委托给基础 class 实现来进行正常格式化,然后是其他东西。在您的设置中,此方法的关键问题是它不适用于非数字输出。例如,输出字符串不会重置任何内容。
可以设置格式化标志
std::unitbuf
在每个 [正确编写的] 输出运算符后导致刷新。刷新被转换为对pubsync()
的调用,并最终在流std::basic_streambuf<cT>
上调用sync()
。sync()
函数可以被覆盖。也就是说,该方法将在设置将输出发送到原始流的 sone 标志时安装自定义流缓冲区和标志std::ios_base::unitbuf
,并在调用sync()
时重置标志。除了有点做作之外,它还有一个问题,就是您无法区分真正的冲洗和自动冲洗(
srd::ios_base::unitbuf
的主要目的是让std::cerr
冲洗)。此外,重置发生在第一个std::ostream::sentry
被销毁时。对于最有可能在格式化第一部分之后的复合值。无论如何,颜色格式化程序使用临时对象。该对象的构造函数可用于重置一些格式化标志。输出运算符在获得 "formatted" 时会在相应的对象上设置必要的流信息(可能使用
mutable
成员)。当然,这意味着设置格式和输出需要在同一条语句中完成。此外,同一语句的所有输出都采用相同的格式。
我不知道在某些输出后处理格式化的任何其他方法。 None 的方法效果特别好。我宁愿使用类似警卫的方法来处理 set/unset 标志,而不是试图变得聪明。