Search code examples
c++templatesboost-logstream-operators

How to define output stream operator for boost log and a custom type


I was able to define the output stream operator for a simple struct, however, not for std::array. The following code fails to compile. What is wrong and how can I fix it?

#include <array>
#include <iostream>
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>

using hash_t = std::array< unsigned char, 32 >;

std::ostream& operator<< ( std::ostream& os, hash_t const& arr )
{
    os << "ole!";
    return os;
}

int main(int, char*[])
{
    hash_t arr;
    std::cerr << arr << std::endl; // complies cleanly
    BOOST_LOG_TRIVIAL(debug) << arr; // Error
    return 0;
}

Here come the errors.

GCC (boost 1.55, gcc-4.9.2):

In file included from /usr/include/boost/log/sources/record_ostream.hpp:31:0,
                 from /usr/include/boost/log/trivial.hpp:23,
                 from trival.cpp:4:
/usr/include/boost/log/utility/formatting_ostream.hpp: In instantiation of ‘boost::log::v2s_mt_posix::basic_formatting_ostream<CharT, TraitsT, AllocatorT>& boost::log::v2s_mt_posix::operator<<(boost::log::v2s_mt_posix::basic_formatting_ostream<CharT, TraitsT, AllocatorT>&, const T&) [with CharT = char; TraitsT = std::char_traits<char>; AllocatorT = std::allocator<char>; T = std::array<unsigned char, 32ul>]’:
trival.cpp:18:30:   required from here
/usr/include/boost/log/utility/formatting_ostream.hpp:710:19: error: cannot bind ‘boost::log::v2s_mt_posix::basic_formatting_ostream<char>::ostream_type {aka std::basic_ostream<char>}’ lvalue to ‘std::basic_ostream<char>&&’
     strm.stream() << value;
                   ^
In file included from /usr/include/c++/4.9/iostream:39:0,
                 from trival.cpp:2:
/usr/include/c++/4.9/ostream:602:5: note: initializing argument 1 of ‘std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = std::array<unsigned char, 32ul>]’
     operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x)

Clang error (boost 1.64, clang-800.0.42.1):

In file included from /usr/local/include/boost/log/trivial.hpp:23:
In file included from /usr/local/include/boost/log/sources/record_ostream.hpp:36:
/usr/local/include/boost/log/utility/formatting_ostream.hpp:878:19: error: invalid operands to binary expression ('ostream_type' (aka
      'basic_ostream<char, std::__1::char_traits<char> >') and 'std::__1::array<unsigned char, 32>')
    strm.stream() << value;
    ~~~~~~~~~~~~~ ^  ~~~~~
/usr/local/include/boost/log/sources/record_ostream.hpp:390:51: note: in instantiation of function template specialization
      'boost::log::v2_mt_posix::operator<<<boost::log::v2_mt_posix::basic_formatting_ostream<char, std::__1::char_traits<char>, std::__1::allocator<char> >,
      std::__1::array<unsigned char, 32> >' requested here
    static_cast< formatting_ostream_type& >(strm) << value;
                                                  ^
/Users/adam/GitPen/BoostLog/trival/trival.cpp:18:27: note: in instantiation of function template specialization
      'boost::log::v2_mt_posix::operator<<<boost::log::v2_mt_posix::basic_record_ostream<char>, std::__1::array<unsigned char, 32> >' requested here
        BOOST_LOG_TRIVIAL(debug) << arr;

Solution

  • You may not like this answer.

    To get this to work properly at the moment you need to add your operator<< to namespace std:

    namespace std{
       std::ostream& operator<< ( std::ostream& os, const hash_t& arr )
       {/*...*/}
    }
    

    because of how ADL works, it is going to only consider the operator<< and its specializations within namespace std

    You wont like it because adding this function to namespace std is not a legal thing to do:

    [namespace.std]

    The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified. A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.

    Maybe the easiest thing to do would be to inherit from (and do nothing else) the std type instead:

    struct hash_t : std::array< unsigned char, 32 >{};