views:

32

answers:

3

I have a class:

class foo {
private:
    std::string data;
public:
    foo &append(const char* str, size_t n) { data.append(str,n); }

    // for debug output
    template <typename T>
    friend T& operator<< (T &out, foo const &f);

    // some other stuff
};

template <typename T>
T& operator<< (T &out, foo const &f) {
    return out << f.data;
}

I want this to work with any class that provides the << operator.

This works fine with std::cout as in:

std::cout << fooObject;

But the following fails:

BOOST_AUTO_TEST_CASE( foo_append_and_output_operator )
{
    // fooObject is accessable here
    const char* str = "hello";
    fooObject.append(str, strlen(str));

    output_test_stream output;
    output << fooObject;

    BOOST_CHECK( output.is_equal(str) );
}

g++ tells me that:

In function ‘T& operator<<(T&, const foo&) 
    [with T = boost::test_tools::output_test_stream]’:
error: invalid initialization of reference of type
    ‘boost::test_tools::output_test_stream&’ from expression of type
    ‘std::basic_ostream<char, std::char_traits<char> >’

What's going on?

I'm using Boost 1.34.1 on Ubuntu 8.04.

A: 

Is it a typo? You wrote

foo.append(str, strlen(str));

but foo is the name of the class and not an object.

mkluwe
Typo, foo is an object, see my edit.
Robert S. Barnes
+1  A: 

You may know that already, but using output_test_stream as an std::ostream works:

class foo {
    // [...]
    friend
    std::ostream& operator<< ( std::ostream &os, const foo &f );
};

std::ostream& operator<< ( std::ostream &os, const foo &f ) {
    return os << f.data;
}
mkluwe
Great, but why doesn't it work when `operator<<` is templated?
Robert S. Barnes
Well, it looks like `output << fooObject` returns a std::ostream, unfortunately. Thus, the template function cannot be instantiated. I think Space_C0wb0y has put some effort to look into the details.For your problem, my proposed code should be the solution.
mkluwe
Thanks, but while that's a solution to my problem, it's not an answer to my question.
Robert S. Barnes
+2  A: 

So I think I have an explanation, but no solution yet. output_test_stream implements its stream functionality by subclassing wrap_stringstream. The insertion-operator for this is a free function-template that looks like this:

template <typename CharT, typename T>
inline basic_wrap_stringstream<CharT>&
operator<<( basic_wrap_stringstream<CharT>& targ, T const& t )
{
    targ.stream() << t;
    return targ;
}

// ... further down in the same header

typedef basic_wrap_stringstream<char>       wrap_stringstream;

Your operator is called with output_test_stream as the stream-type, and that makes this it's return-type. Your operator then calls the above operator, and just propagates the return value. The return value of the above operator however is a superclass of the returntype of your operator. When compiler tries to create the reference you want to return, it chokes, because it cannot initialize a reference to a subclass from a reference to a superclass, even if both refer to the same object. That make any sense?

Space_C0wb0y
Yeah, kinda makes sense... I'm looking at the Boost header files now to try and understand it a bit better.
Robert S. Barnes
Do you know if this is still a problem in newer versions of Boost?
Robert S. Barnes
Well you'd just have to compare the header `wrap_stringstream.hpp`. You can just change the version number in the link above to the most recent one, I think it is 1.43.0.
Space_C0wb0y