如何使我的函数对重载的 iostream 提取运算符具有粘性

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

本文关键字:提取 iostream 运算符 有粘性 重载 何使 我的 函数      更新时间:2023-10-16

>我正在做一个学校项目,我需要经常更改文本颜色。项目目标是当前仅适用于 Windows 的控制台应用。将代码块与 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";

我想要的是像setwsetprecision等功能。所以我打开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确实坚持了程序的其余部分。为什么??(尽管它 100% 类似于 setw (。

我怎样才能让setcoloursetw ???一样我需要此功能来使我的程序更加干净和合乎逻辑。

我也发现了这个:哪些 iomanip 机械手是粘性的

但是那里的答案和评论只会让我感到困惑。当然,setw调用 cout.width(0(,但调试显示没有这样的调用,也没有在iomanip.h中找到这样的代码行。也不明白那里的答案。请解释一下。

编辑

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

如何让我的SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0)(在setcolour的上下文中(每次都被调用???我应该如何处理这个问题?

宽度是专门处理的:所有内置运算符都会在输出对象后重置宽度。在代码或调试器中看不到相应的width(0)调用这一事实并不意味着它不存在!它可以是内联的,而不是命中断点,例如。关于代码,您需要查看,例如,在std::num_put<...>的实现中:实际的输出运算符不包含代码,但do_put()函数包含代码。

要重置其他格式标志,您需要挂接到每个输出操作后调用的某个操作。但是,机会不多:流不支持在每个对象之后进行通用自定义。以下是可以做的事情,但我会重新评论,让格式标志保持粘性。将宽度特别化可以说是一个错误:

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

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

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

    除了有点做作之外,它还存在一个问题,即您无法区分真正的冲洗和自动冲洗(srd::ios_base::unitbuf的主要目的是使std::cerr刷新(。此外,重置发生在销毁的第一个std::ostream::sentry。对于最有可能在格式化第一部分之后的复合值。

  • 无论如何,颜色格式化程序都使用临时对象。此对象的析构函数可用于重置一些交换标志。输出运算符将在相应对象被"格式化"(可能使用 mutable 成员(时在相应对象上设置必要的流信息。当然,这意味着设置格式和输出需要从同一语句完成。此外,同一语句上的所有输出都采用相同的格式。

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

请注意,setw 没有坚持,它是第二行中的重置。如何??

使用 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*)
  • 输出
    • 重载 1-7 的 basic_ostream::operator<<() (在 num_put::put() 的第 3 阶段(
    • operator<<(basic_ostream&, char)operator<<(basic_ostream&, char*)
    • operator<<(basic_ostream&, basic_string&)
    • std::put_money(money_put::put())
    • std::quoted(与输出流一起使用时(