5

我正在做一个需要经常更改文本颜色的学校项目。项目目标是当前仅适用于 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没有粘住,它是在第二行重置。如何 ??(调试显示没有执行额外的调用来重置它。)

但是我的setcolourDID 坚持了程序的其余部分。为什么 ??(尽管它 100% 类似于setw)。

我怎么能setcoloursetw???我需要这个功能来使我的程序更干净和合乎逻辑。

我还发现了这个: 哪些 iomanip 操纵器很粘

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

编辑

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

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

4

2 回答 2

1

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

要重置其他格式化标志,您需要挂钩在每次输出操作后调用的某些操作。但是,机会并不多:流不支持每个对象之后的通用自定义。以下是可以做的事情,但我建议保留格式化标志。将宽度设为特殊可能是一个错误:

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

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

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

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

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

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

于 2015-11-09T08:44:43.497 回答
0

请注意,setw 没有粘住,它在第二行被重置。如何 ??

当您使用 时cout << setcolour(AQUA),您正在对稍后使用的程序状态进行持久更改。

情况并非如此std::setw()std::setw()只设置下一个输出的宽度,然后将宽度重置为零。有关详细信息,请参阅http://en.cppreference.com/w/cpp/io/manip/setw 。特别是,注释

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

  • 输入
    • operator>>(basic_istream&, basic_string&)
    • operator>>(basic_ostream&, char*)
  • 输出
    • 重载 1-7 basic_ostream::operator<<()(在第 3 阶段num_put::put()
    • operator<<(basic_ostream&, char)operator<<(basic_ostream&, char*)
    • operator<<(basic_ostream&, basic_string&)
    • std::put_money(里面money_put::put())
    • std::quoted(与输出流一起使用时)
于 2015-11-09T07:20:23.787 回答