BOOST_CHECK_EQUAL and operator<<
Hello,
I am currently upgrading from boost-1.30.2 to boost-1.32.0 with MSVC 7.1 and I
am running into the following issue:
BOOST_CHECK_EQUAL requires the elements to be compared to provide an operator<<
taking an std::ostream& as first parameter. I always had names lookups problems
with that when the operator was provided by the test and not along with the
element. Until now, I had the operator defined in "boost" namespace (that's
ugly I know) in the test file. It breaks with 1.32.0 because the
problematic function (print_log_value) moved deeped into test tools
namespaces. Then I try to revert to a
normal definition but it does not work either. For instance :
//*******************************
#include
Patrick M?zard wrote:
namespace testns {
struct Test { .... };
namespace {
std::ostream& operator<<(std::ostream& o, Test const& t) { return o<
...
Does not compile and gives me a: C2679: binary 'operator' : no operator defined which takes a right-hand operand of type 'const testns::Test' (or there is no acceptable conversion), the failure being issued by "void boost::test_tools::tt_detail::print_log_value<T>::operator ()(std::ostream &,const T &)"
However, if I define operator<< as a public member of testns::Test it compiles. Same thing if I move it outside the anonymous namespace, with or without static linkage.
I think defining operator<< in the same namespace as the class 'Test' is the only way. Otherwise, it won't be found by ADL and ADL is the only way to boost.test to find your operator<<.
Note these workaround do not work or cannot be applied if the type is declared by a typedef. In this case, I have to define again the operator in namespace boost.test_tools.tt_detail.
If the type is declared by a typedef: typedef foo::C C2; you can define the operator<< in the 'foo' namespace, and that should work. - Volodya
Vladimir Prus
Patrick M?zard wrote:
namespace testns {
struct Test { .... };
namespace {
std::ostream& operator<<(std::ostream& o, Test const& t) { return o<
...
Does not compile and gives me a: C2679: binary 'operator' : no operator defined which takes a right-hand operand of type 'const testns::Test' (or there is no acceptable conversion), the failure being issued by "void boost::test_tools::tt_detail::print_log_value<T>::operator ()(std::ostream &,const T &)"
However, if I define operator<< as a public member of testns::Test it compiles. Same thing if I move it outside the anonymous namespace, with or without static linkage.
I think defining operator<< in the same namespace as the class 'Test' is the only way. Otherwise, it won't be found by ADL and ADL is the only way to boost.test to find your operator<<.
Thank you for your answer. I did not know that ADL treats normal and anonymous
namespaces differently (and cannot find anything related in google.groups or
other mailing lists).
However, it does not work with typedefed types. Here is a new example :
//*********************
#include
Patrick M?zard wrote:
However, it does not work with typedefed types. Here is a new example :
//********************* #include
using boost::unit_test::test_suite; namespace testns {
typedef std::pair
Test; std::ostream& operator<<(std::ostream& o, Test const& t) { return o<
Try writing:
namespace std
{
std::ostream& operator<<(std::ostream& o, std::pair
Vladimir Prus
I think namespace std is the only place that will be searched for operator<< (except for boost:: and for boost::test_tools and boost::test_tools::tt_detail).
Good pick, it works. I would like to know if the original version which defines the operator<< in testns:: is legal or not, but at least your solution will save me a lot of typing. Thank you very much. Patrick Mézard
Vladimir Prus wrote:
Patrick M?zard wrote:
[...]
I think defining operator<< in the same namespace as the class 'Test' is the only way. Otherwise, it won't be found by ADL and ADL is the only way to boost.test to find your operator<<.
Note these workaround do not work or cannot be applied if the type is declared by a typedef. In this case, I have to define again the operator in namespace boost.test_tools.tt_detail.
If the type is declared by a typedef:
typedef foo::C C2;
you can define the operator<< in the 'foo' namespace, and that should work.
That is very unfortunate. Is there no way to have the operator<< lookup occur in the scope of the call to BOOST_CHECK_EQUAL, by moving the wrap_stringstream logic and the call to operator<< into the macro itself ? Cheers, Ian
If the type is declared by a typedef:
typedef foo::C C2;
you can define the operator<< in the 'foo' namespace, and that should work.
That is very unfortunate. Is there no way to have the operator<< lookup occur in the scope of the call to BOOST_CHECK_EQUAL, by moving the wrap_stringstream logic and the call to operator<< into the macro itself ?
No. there are several reasons why it couldn't be done: 1. Printing log value in the scope of call would cause multiple value evaluation. Imagine what if I write BOOST_CHECK_EQUAL( i++, 2 ); 2. I need extra level of indirection to be able to prevent value from being printed (BOOST_TEST_DONT_PRINT_LOG_VALUE(type)) 3. I need extra level of indirection to be able to do custom printing for some types You have several choices 1. You could define operator<< in namespace foo 2. You could use BOOST_TEST_DONT_PRINT_LOG_VALUE(your type) on global scope to prevent values of this type being printed at all 3. You could write struct C2 : foo::C {}; And unless you use non-default constructor it will work. Gennadiy
Gennadiy Rozental
You have several choices
1. You could define operator<< in namespace foo
It does not work because of the typedef (see below). Please see my first answer to Vladimir. It works if it is defined in namespace std:: however.
2. You could use BOOST_TEST_DONT_PRINT_LOG_VALUE(your type) on global scope to prevent values of this type being printed at all
Okay, but since I am the one writing the tests, I would simply use BOOST_CHECK().
3. You could write struct C2 : foo::C {}; And unless you use non-default constructor it will work.
Maybe, but it seems much more work just to use BOOST_CHECK_EQUAL().
I could understand the anonymous namespace and typedefs have issues with ADL and
there are workaround to be found. For those interested with ADL rules, I finally
got an old post from llewely on comp.lang.c+.moderated on a similar problem
stating that (look for "Re: stream iterators and operator<< " in google.groups):
<quote>
A typedef name is not a type. The occurance of a typedef name in a
scope does not add that scope to consideration in
argument-dependent lookup. [3.4.2/2]
Unqualified lookup does not occur because inside the definition of the
function called by '*it = *v.begin()', operator<< is used for a
function call. Instead, argument-dependent lookup is
used. [3.4.2/1]
Argument dependent lookup will lookup names in the classes and
namespaces associated with the argument types. pair
namespace testns {
struct Test { Test(std::string const& s_) : s(s_) {}
bool operator==(Test const& t) const { return s==t.s; }
std::string s; };
namespace {
std::ostream& operator<<(std::ostream& o, Test const& t) { return o<
void free_test_function() { Test t1("test1"), t2("test2"); BOOST_CHECK_EQUAL(t1, t2); }
} //namespace
} //namespace testns
Why do you need unnamed namespace? Remove it and it will all work. Gennadiy
Gennadiy Rozental
Why do you need unnamed namespace? Remove it and it will all work.
The type being tested belongs to a library. I do not need the operator<< at the library level, only in the test for BOOST_CHECK_EQUAL support. So I define the operator at in the test scope, in an anonymous namespace because there is no need to export the symbol. That's good practice for me, maybe I am wrong ? Patrick Mézard
participants (4)
-
Gennadiy Rozental
-
Ian McCulloch
-
Patrick Mézard
-
Vladimir Prus