7

When attempting to do a BOOST_CHECK_EQUAL(pair, pair), gcc doesnt find the stream operator for pair, inspite of declaring it. The funny thing is that std::out finds the operator.

ostream& operator<<(ostream& s, const pair<int,int>& p) {
    s << '<' << p.first << ',' << p.second << '>';
    return s;
}


BOOST_AUTO_TEST_CASE(works)
{
    pair<int,int> expected(5, 5);
    pair<int,int> actual  (5, 5);
    std::cout << expected << std::endl;
    std::cout << actual   << std::endl;
    BOOST_CHECK(actual == expected);
}

BOOST_AUTO_TEST_CASE(no_work)
{
    pair<int,int> expected(5, 5);
    pair<int,int> actual  (5, 5);
    BOOST_CHECK_EQUAL(actual, expected);
}

This doesnt compile with the error:

...  instantiated from here
../boost-atp/release/include/boost/test/test_tools.hpp:326:9: error: no match for ‘operator<<’ in ‘ostr << t’
nishantjr
  • 1,788
  • 1
  • 15
  • 39
  • The *new* way of defining your own printing for custom types is explained here: https://stackoverflow.com/a/44810846/1617295 , and [this is the official documentation](http://www.boost.org/doc/libs/1_66_0/libs/test/doc/html/boost_test/test_output/test_tools_support_for_logging/testing_tool_output_disable.html) of that feature. – Raffi Feb 14 '18 at 19:49
  • @Raffi That question looks like a duplicate of this one, has less details. Maybe it makes sense to mark it as a duplicate and move your answer here so that we don't make users jump about? I can accept it too. Thanks! – nishantjr Feb 15 '18 at 06:03

3 Answers3

12

Putting operator<< in std like Remus's answer is undefined behavior in the C++ 14 draft (N4296 section:17.6.4.2.1). Boost provides a hook (used by this answer) and you can write:

namespace boost
{
    namespace test_tools
    {
        template<typename T,typename U>
        struct print_log_value<std::pair<T, U> >
        {
            void operator()(std::ostream& os, std::pair<T, U> const& pr)
            {
                os << "<" << std::get<0>(pr) << "," << std::get<1>(pr) << ">";
            }
        };
    }
}

print_log_value is a template so if you are not declaring a templated value like pair<T,U>, you will need to write something like:

template<>
struct print_log_value<MyType>{ /* implementation here*/ };

Edit

If you are using boost 1.59 or later you need to use namespace boost::test_tools::tt_detail instead. That is, the code needs to start:

namespace boost
{
    namespace test_tools
    {
        namespace tt_detail
        {
Community
  • 1
  • 1
Eponymous
  • 6,143
  • 4
  • 43
  • 43
10

Try putting the operator itself in the std namespace:

namespace std
{
  ostream& operator<<(ostream& s, const pair<int,int>& p) {
    s << '<' << p.first << ',' << p.second << '>';
    return s;
  }
}

Update: perhaps this is why the ADL fails (at least on llvm):

Just like before, unqualified lookup didn't find any declarations with the name operator<<. Unlike before, the argument types both contain class types: one of them is an instance of the class template type std::basic_ostream, and the other is the type ns::Data that we declared above. Therefore, ADL will look in the namespaces std and ns for an operator<<. Since one of the argument types was still dependent during the template definition, ADL isn't done until the template is instantiated during Use, which means that the operator<< we want it to find has already been declared. Unfortunately, it was declared in the global namespace, not in either of the namespaces that ADL will look in!

Remus Rusanu
  • 288,378
  • 40
  • 442
  • 569
  • I dont understand why ADL doesnt apply within BOOST_CHECK_EQUAL. Does Boost do something to stop that from happening? – nishantjr Jun 11 '12 at 08:27
  • @njr: I looked into it some time ago but didn't found the root cause. Afaik gcc does ADL, and so does llvm. – Remus Rusanu Jun 11 '12 at 08:35
  • 1
    This works for me, but it seems to be undefined behavior (http://en.cppreference.com/w/cpp/language/extending_std ). Looking, I discovered the following answer that gives a way to avoid the undefined behavior http://stackoverflow.com/a/17573165/309334 – Eponymous Nov 27 '15 at 19:58
  • @Eponymous wouldn't this case fall under *It is allowed to add template specializations for any standard library template to the namespace std only if the declaration depends on a user-defined type and the specialization satisfies all requirements for the original template* ? – Remus Rusanu Nov 27 '15 at 20:01
  • The example of "illegal use" in cppreference uses `operator+(std::pair,std::pair) which is almost exactly what this solution does. I think I need to get out a copy of the standard. – Eponymous Nov 27 '15 at 20:09
  • 1
    *Good news*: The example given is not in the C++14 draft standard (N4296 section:17.6.4.2.1). *Bad news*: the wording of the standard is similar to what you quoted. The only things listed as allowed in the standard are template specializations. `ostream& operator<<(ostream&, pair)` is not a template specialization, it is a function overload. – Eponymous Nov 27 '15 at 20:34
  • 1
    The wording is: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. – Eponymous Nov 27 '15 at 20:36
0

I was looking for something similar, a way to customize the output string to print integers in hex. Injecting an operator into the std namespace would work, but every BOOST_CHECK in my test would be printed in hex.

So I injected some custom operators in the boost namespace that I could control with some global bools.

See my answer here boost-check-fails-to-compile-operator-for-custom-types.

Community
  • 1
  • 1
Nick
  • 2,342
  • 28
  • 25