Search code examples
c++stringstllog4cxx

How to use an output stream operator defined in a header


I would like to be able to append the content of any std::vector<T> to an output stream. I've found this code:

#ifndef DEBUG_H_
#define DEBUG_H_

#include <vector>

template < class T >
std::ostream& operator << (std::ostream& os, const std::vector<T>& v)
{
    os << "[";
    for (typename std::vector<T>::const_iterator ii = v.begin(); ii != v.end(); ++ii)
    {
        os << " " << *ii;
    }
    os << "]";
    return os;
}


#endif /* DEBUG_H_ */

and put in in a header Debug.h. How can I use this operator troughout my project?

EDIT: I have verified that this works in a unit test:

#include "Debug.h"

TEST_F(AuxGTest, testVectorDebug) {
    std::vector<int> vec(10, 42);
    std::cout << "vec: " << vec << std::endl;
}

But using it with log statements of log4cxx does not work:

#include <log4cxx>
#include "Debug.h"

namespace Foo {
    class Bar { 
        void foo() {
            std::vector<int> vec(10, 42);
            DEBUG("vec: " << vec);
        }

    }

}

This results in the following compiler message:

/usr/local/Cellar/log4cxx/0.10.0/include/log4cxx/helpers/messagebuffer.h:190:47: error: cannot bind 'std::basic_ostream<char>' lvalue to 'std::basic_ostream<char>&&'

Solution

  • Where are you trying to use it? As declared, it's in the global namespace, so it won't be found by ADL unless T is a type defined in the global namespace. And it won't be found by normal lookup if you're in a namespace other than global namespace, and there is an operator<< in that namespace (which would hide it). It also won't be found if invoked in a template, if any of the arguments are dependent, since dependent name lookup only uses ADL.

    And of course, you really don't want to do this except in toy programs. Different uses of std::vector will require different output formats; it's far better to wrap the std::vector in a class, and define the operator<< for the class, for each different semantic use. (For toy programs, you can define the operator in namespace std. Undefined behavior, but since no one else will ever see the code, or have to maintain it, and it's not the end of the world if it doesn't work...)

    EDIT:

    Since it seems that you're using this for some sort of tracing or debugging: this is one case where it does make sense to support a unified output for std::vector, since it is used to output internal state. On the other hand, it is usual in this case for the ostream to be wrapped, something like:

    class Logger
    {
        std::ostream* myDest;
    public:
        Logger( std::ostream* dest )
            : myDest( dest )
        {
        }
    
        template <typename T>
        Logger& operator<<( T const& value )
        {
            if ( myDest != NULL ) {
                *myDest << value;
            }
            return *this;
        }
    };
    

    This allows runtime configuration of the logging (plus automatic insertion of things like __FILE__ and __LINE__, if you use a macro to get the instance of Logger); your macro DEBUG might be something like:

    #define DEBUG getLogger( TRACELEVEL_DEBUG, __FILE__, __LINE__ )
    

    where getLogger is a global function which returns a Logger initialized with the correct ostream (or a null pointer, if logging isn't active for that level). Once you do this, you can add special functions, like:

    template <typename T>
    Logger& Logger::operator<<( std::vector<T> const& value )
    {
        if ( myDest != NULL ) {
            //  your code here...
        }
        return *this;
    }
    

    Since one of the arguments the the << operator will be an instance of Logger, ADL will find the operator, regardless.