Я пытаюсь сделать потоковый манипулятор для цвета для использования с выводом на консоль. Работает, меняя цвет текста и фона:
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<>
будет приведен к типу, с которым я сравниваю при передаче в функцию, давая ложные срабатывания.
В конечном итоге это привело меня к моему ответу ниже (аргументы шаблона вариативного шаблона? Ух, это глоток!) Однако есть пара проблем:
std::ostringstream
, который имеет 3 вместо 2), так как он не проходит через сигнатуру функции.std::cout
то же самое, что и в случае с потоком строк.Я призываю людей публиковать любые другие решения, надеюсь, лучше, чем у меня, и очень надеюсь, что то, что работает по крайней мере VS11.
Вот черта для обнаружения 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?
Вот что я придумал после долгих испытаний:
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
, который имеет такое же количество параметров шаблона, приводит к сбою статического утверждения.Пожалуйста, не стесняйтесь улучшать это любым доступным способом.