确保参数是控制台的输出流

时间:2012-11-01 07:19:39

标签: c++ inheritance c++11 variadic-templates typetraits

我正在尝试为颜色制作一个流操纵器,以便与输出到控制台一起使用。它有效,改变了文字和背景的颜色:

std::cout << ConColor::Color::FgBlue << 123 << "abc"; //text is blue, sticky

问题在于签名:

std::ostream &FgBlue(std::ostream &);

此签名允许派生类,例如std::ostringstream,但无法更改字符串流的颜色。无论是否使用这样的参数调用,该函数都会改变控制台的颜色。

因此,我想确保论证与std::coutstd::wcout等等有关。我希望在添加更多std::ostream个对象的情况下这是一般性的。在未来的标准中。

我尝试了许多涉及std::is_samestd::is_base_of的事情,当前者不起作用时,只是为了最终意识到这是毫无意义的,因为继承自std::basic_ostream<>的任何参数类型都将被转换传递给函数时我正在比较的类型,给出误报。

这最终导致我在下面给出了我的答案(可变参数模板模板参数?哇,这是一个满口!)但是有几个问题:

  • 编译器必须支持可变参数模板。我更希望解决方案适用于MSVC。
  • 编译器在使用具有不同数量的模板参数的派生类(例如std::ostringstream,其中有3而不是2)的情况下给出了神秘错误,因为它没有通过该函数签名。
  • 例如,可以将stdout重定向到文件,因此即使参数为std::cout,也会发生与stringstream情况相同的事情。

我鼓励人们发布任何其他解决方案,希望比我更好,并且真的希望至少能与VS11一起使用。

2 个答案:

答案 0 :(得分:1)

这是经过大量试验后我想出来的:

template<template<typename...> class T, typename... U>
void foo(T<U...> &os) {
    static_assert(
        std::is_same<
            std::basic_ostream<U...>, 
            typename std::remove_reference<decltype(os)>::type
        >::value, 
        "Argument must be of type std::basic_ostream<T, U>."
    );
    //...
}

可以找到包含以下每项测试的源代码here 可以找到源代码替换具有类似的自制类型的类型,这些类型更明确并提供更多自由(例如,实例化),这可能对测试更有用,可以找到here

  • 传入std::coutstd::wcout使其编译正常。
  • 传入std::ostringstream的实例会导致它抱怨模板参数的数量。
  • 传入具有相同数量模板参数的std::fstream实例会导致静态断言失败。
  • 传入自制的2参数模板类会导致静态断言失败。

请随时以任何方式改进。

答案 1 :(得分:1)

这是检测std::basic_ostream实例化的特征:

template<typename T> struct is_basic_ostream {
  template<typename U, typename V>
  static char (&impl(std::basic_ostream<U, V> *))[
    std::is_same<T, std::basic_ostream<U, V>>::value ? 2 : 1];
  static char impl(...);
  static constexpr bool value = sizeof(impl((T *)0)) == 2;
};

用作:

template<typename T>
void foo(T &) {
  static_assert(is_basic_ostream<T>::value,
    "Argument must be of type std::basic_ostream<T, U>.");
}

我们使用模板参数推导来推断(非正确的)basic_ostream基类上的模板参数(如果有的话)。作为更通用的解决方案,用单个可变参数替换UV将允许在支持可变参数模板参数的编译器上编写通用is_instantiation_of特征。


要检测stdout是否通过管道传输到文件(当然只能在运行时检测到),请使用isatty;见how to use isatty() on cout, or can I assume that cout == file descriptor 1?