Убедитесь, что аргумент является выходным потоком для консоли

Я пытаюсь сделать потоковый манипулятор для цвета для использования с выводом на консоль. Работает, меняя цвет текста и фона:

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

Проблема с подписью:

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

Эта подпись учитывает производные классы, такие как std::ostringstream также, но нет способа изменить цвет струнного потока. Функция изменила бы цвет консоли независимо от того, была ли она вызвана с таким аргументом.

Поэтому я хочу убедиться, что аргумент является чем-то вроде std::cout, std::wcoutи т. д. Я бы предпочел, чтобы это было в общем случае std::ostream объекты добавляются в будущем стандарте.

Я пробовал много вещей, связанных с std::is_same а также std::is_base_of, когда первый не будет работать, просто чтобы в конечном итоге понять, что это было бессмысленно, потому что любой тип аргумента, наследующий от std::basic_ostream<> будет приведен к типу, с которым я сравниваю при передаче в функцию, давая ложные срабатывания.

В конечном итоге это привело меня к моему ответу ниже (аргументы шаблона вариативного шаблона? Ух, это глоток!) Однако есть пара проблем:

  • Компилятор должен поддерживать переменные шаблоны. Я бы предпочел решение работы на MSVC.
  • Компилятор выдает загадочные ошибки в случае, если производный класс с другим числом аргументов шаблона (например, std::ostringstream, который имеет 3 вместо 2), так как он не проходит через сигнатуру функции.
  • Можно перенаправить стандартный вывод, скажем, в файл, так что даже если аргумент std::coutто же самое, что и в случае с потоком строк.

Я призываю людей публиковать любые другие решения, надеюсь, лучше, чем у меня, и очень надеюсь, что то, что работает по крайней мере VS11.

4

Решение

Вот черта для обнаружения 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 базовый класс, если есть. Как более общее решение, заменяя U а также V с одним параметром variadic позволит написать общий is_instantiation_of черта на компиляторах, которые поддерживают параметры шаблона variadic.


Чтобы определить, передается ли стандартный вывод в файл (который, конечно, может быть обнаружен только во время выполнения), используйте isatty; увидеть Как использовать isatty () на Cout, или я могу предположить, что Cout == дескриптор файла 1?

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>.");
//...
}

Исходный код, содержащий каждый из следующих тестов можно найти Вот.
Можно найти исходный код, заменяющий типы аналогичными самодельными, более явными и более свободными (например, создание экземпляров), которые могут быть более полезными для тестирования. Вот.

  • Проходя в std::cout а также std::wcout делает компиляцию нормально.
  • Проходя в случае std::ostringstream заставляет его жаловаться на количество аргументов шаблона.
  • Проходя в случае std::fstream, который имеет такое же количество параметров шаблона, приводит к сбою статического утверждения.
  • Передача в самодельном 2-параметрическом шаблонном классе приводит к сбою статического утверждения.

Пожалуйста, не стесняйтесь улучшать это любым доступным способом.

1

По вопросам рекламы [email protected]