Question

How to determine weather ostream is a file or a console stream. In the following program I want to print "Hello file!" while writing to a file and "Hello console!" while writing to console. What condition should I specify at line 17?

#include <fstream>
#include<iostream>
#include <string>
using namespace std;

class A{
public:
        A(string msg):_str(msg){}
        string str()const {return _str;};
private:
        string _str;
};

ostream & operator << (ostream & os, const A & a)
{
        if (os is ofstream) //this is line 17
                os << "Hello file! " << a.str() << endl;
        else
                os << "Hello console! " << a.str() << endl;

        return os;
}

int main()
{
        A a("message");
        ofstream ofile("test.txt");
        if (!ofile)
                cerr << "Unable to open file";
        else
                ofile << a;  // "Hello file"

        cout << a << endl; // "Hello console"
}
Was it helpful?

Solution

Maybe not pretty, but

std::streambuf const * coutbuf = std::cout.rdbuf();
std::streambuf const * cerrbuf = std::cerr.rdbuf();

ostream & operator << (ostream & os, const A & a)
{
        std::streambuf const * osbuf = os.rdbuf();

        if ( osbuf == coutbuf || osbuf == cerrbuf )
                os << "Hello console! " << a.str() << endl;
        else
                os << "Hello file! " << a.str() << endl;

        return os;
}

We could use &os == &std::cout, but the Standard output might be redirected to file, so I think it is better to use the streambuf object instead. (See this answer for better understanding as to how the redirection works, and why comparing streambuf solves the problem safely! )

OTHER TIPS

You could (ab)use tellp(), which returns -1 if the stream does not have a position:

bool isConsoleStream(ostream const& stream)
{
    return stream.tellp() == -1;
}

Of course, there could be other streams that return -1 for this function, so use with caution.

There is no portable means. Under Unix, you can do:

if ( (&os == &std::cout && isatty( STDOUT ))
        || (&os == &std::cerr && isatty( STDERR ))
        || (&os == &std::clog && isatty( STDERR )) ) }
    //  is a terminal...
}

Under Windows, the isatty becomes _isatty, and I'm not sure that the macros exist (but I suspect that they do).

Of course, this supposes that you don't do things to confuse it in your code. Something like:

std::ostream s( std::cout.rdbuf() );

for example, or:

std::cout.rdbuf( &someFileBuf );

Or even:

std::ofstream s( "/dev/tty" );  //  (or "CONS" under Windows).

But it's about as close as you can get without the actual fd from the filebuf.

One is a ofstream and the other is a ostream. Just have two methods.

#include <iostream>
#include <string>
#include <fstream>

class A {
    std::string s;
public:
    A(const std::string& s) : s(s){}
    std::string str() const {return s;}
};


ostream & operator << (std::ostream & os, const A & a)
{
    return os << "console: " << a.str() << std::endl;
}

ofstream & operator << (std::ofstream & os, const A & a)
{
    return os << "file: " << a.str() << std::endl;
}

int main()
{
    A a("hello world");
    std::cout << a << endl;
}

This works on Visual Studio 2012

if (typeid(os) == typeid(ofstream)) //this is line 17

But an ostream could be something that isn't an ofstream or the console so you'd have to be careful.

Function to check if a C++ character stream is connected to a terminal/console/tty.

Ideally, we would use the file descriptor under-laying the stream buffer of the C++ stdio stream (cin, cout, cerr or clog). However, there is no way to retrieve the under-laying file descriptor. So, we use the fact that at program start-up the stdio stream buffers are connected to the program's standard input and output.

This function only works under the following conditions:

  1. The stream buffers of the start-up C++ stdio streams must not change. Because the addresses of the stream buffers of the start-up C++ stdio streams are used as identifiers. For instance by deleting them and then allocating a new stream buffer that has the same address as one of these stream buffers of the start-up C++ stdio streams.

  2. The program's stdio must not change after program start-up. Because the TTY statuses of the stdio stream buffers are stored at program start-up. For instance if at start-up the std. out is connected to a terminal and later it is redirected to a pipe or file by something external to the program. [Instead of storing the TTY statuses at start-up you could retrieve them at run-time, but then you must make sure that your program (and all libraries it uses) does not change the stdio file descriptors (0, 1 and 2). Rember that the stdio stream buffers most likely use other (duplicate) file descriptors.]

Code:

#include <iostream>
extern "C" {
#ifdef _WIN32
# include <io.h>        // for: _isatty()
#else
# include <unistd.h>    // for: isatty()
#endif
}

// Stdio file descriptors.
#ifndef STDIN_FILENO
# define STDIN_FILENO   0
# define STDOUT_FILENO  1
# define STDERR_FILENO  2 
#endif


// Store start-up addresses of C++ stdio stream buffers as identifiers.
// These addresses differ per process and must be statically linked in.
// Assume that the stream buffers at these stored addresses
//  are always connected to their underlaying stdio files.
static const  streambuf* const StdioBufs[] = {
    std::cin.rdbuf(),  std::cout.rdbuf(),  std::cerr.rdbuf(),  std::clog.rdbuf()
};
static const wstreambuf* const StdioWBufs[sizeof(StdioBufs)/sizeof(StdioBufs[0])] = {
    std::wcin.rdbuf(), std::wcout.rdbuf(), std::wcerr.rdbuf(), std::wclog.rdbuf()
};

// Store start-up terminal/TTY statuses of C++ stdio stream buffers.
// These statuses differ per process and must be statically linked in.
// Assume that the statuses don't change during the process life-time.
static const bool StdioTtys[sizeof(StdioBufs)/sizeof(StdioBufs[0])] = {
#ifdef _WIN32
    _isatty(STDIN_FILENO), _isatty(STDOUT_FILENO), _isatty(STDERR_FILENO), _isatty(STDERR_FILENO)
#else
     isatty(STDIN_FILENO),  isatty(STDOUT_FILENO),  isatty(STDERR_FILENO),  isatty(STDERR_FILENO)
#endif
};

// Is a Terminal/Console/TTY connected to the C++ stream?
// Use on C++ stdio chararacter streams: cin, cout, cerr and clog.
bool isTTY(const ios& strm)
{
    for(unsigned int i = 0; i < sizeof(StdioBufs)/sizeof(StdioBufs[0]); ++i) {
        if(strm.rdbuf() == StdioBufs[i])
            return StdioTtys[i];
    }
    return false;
}

// Is a Terminal/Console/TTY connected to the C++ stream?
// Use on C++ stdio wide-chararacter streams: wcin, wcout, wcerr and wclog.
bool isTTY(const wios& strm)
{
    for(unsigned int i = 0; i < sizeof(StdioWBufs)/sizeof(StdioWBufs[0]); ++i) {
        if(strm.rdbuf() == StdioWBufs[i])
            return StdioTtys[i];
    }
    return false;
}

Note: I've only tested it on Linux.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top