Make sure the argument is console output

I am trying to make a flow manipulator for color for use with console output. It works by changing the color of text and background:

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

The problem is the signature:

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

This signature also allows you to use derived classes such as std::ostringstream , but there is no way to change the color of the string stream. The function will change the color of the console regardless of whether it was called with this argument.

So I want the argument to be something like std::cout , std::wcout , etc. I would prefer it to be generic if more std::ostream objects are added to the next standard.

I tried a lot of things related to std::is_same and std::is_base_of when the first one didn't work, just to make it senseless, because any type of argument inheriting from std::basic_ostream<> will be passed to the type I'm comparing with when the function is passed, giving false positives.

This ultimately led me to my answer below (template variational template arguments: “Wow, this is a sip!”) There are several problems:

  • The compiler must support variable templates. I would prefer the solution to work on MSVC.
  • The compiler gives critical errors when using a derived class with a different number of template arguments (for example, std::ostringstream , which has 3 instead of 2), since it does not pass by the signature of the function.
  • You can redirect stdout, say, to a file, so even if the argument is std::cout , the same thing happens as with a string stream.

I urge people to post any other solutions, hopefully better than mine, and really hope something that works, at least with VS11.

+4
source share
2 answers

Here is a sign to detect std::basic_ostream instances:

 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; }; 

Use as:

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

We use the output of the template argument to output the template parameters ( basic_ostream base class), if any. As a more general solution, replacing U and V with one variational parameter would allow us to write a common attribute is_instantiation_of for compilers that support variational template parameters.


To determine if stdout is passed to a file (which can only be detected at runtime), use isatty ; see how to use isatty () in cout, or can I assume that cout == file descriptor 1?

+1
source

This is what I came up with after a lot of samples:

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

The source code containing each of the tests below can be found here .
Source code that replaces types with similar home-made ones, more explicit and offering more freedom (such as instantiation), which may be more useful for testing, can be found.

  • Passing to std::cout and std::wcout makes it compiled.
  • Passing in an instance of std::ostringstream makes it complain about the number of arguments to the template.
  • Passing an instance of std::fstream , which has the same number of template parameters, will cause the static statement to fail.
  • Passing in a makeshift 2-parameter pattern causes a static statement to fail.

Please feel free to improve this as you can.

+1
source

Source: https://habr.com/ru/post/1443377/


All Articles