How to report a user-defined type?

132 views
Skip to first unread message

Martin Moene

unread,
Sep 17, 2014, 4:01:04 PM9/17/14
to catch...@googlegroups.com
I'd like to report a user-defined type.

I tried below code, however that reports {?} instead of the book type-specific way.

How should it be done?


// C++11 - report a user-defined data type.

#define CATCH_CONFIG_MAIN  // This tells Catch to provide a main() - only do this in one cpp file
#include "catch.hpp"

namespace ns {

struct book
{
   
using text = std::string;

    text author
;
    text title
;
    text isbn
;

    book
( text author, text title, text isbn_ )
   
: author( author ), title( title ), isbn( isbn_ ) {}

   
friend bool operator==( book const & lhs, book const & rhs )
   
{
       
return lhs.author == rhs.author
           
&& lhs.title == rhs.title
           
&& lhs.isbn == rhs.isbn;
   
}

   
friend bool operator!=( book const & lhs, book const & rhs )
   
{
       
return ! ( lhs == rhs );
   
}
};

} // namespace ns

// provide stream operator for ns::book:

namespace Catch {

std
::ostream & operator<<( std::ostream & os, ::ns::book const & b )
{
   
return os << "[book: " << toString(b.author) << ", " << toString(b.title) << ", " << toString(b.isbn) << "]";
}

} // namespace Catch

ns
::book atocpp{ "Bjarne Stroustrup", "A Tour of C++.", "978-0-321-95831-0" };
ns
::book tcpppl{ "Bjarne Stroustrup", "The C++ Programming Language.", "978-0-321-56384-2" };

TEST_CASE
( "A book reports via the book-specific operator<<()" )
{
    REQUIRE
( atocpp != tcpppl );
    REQUIRE
( atocpp == tcpppl );
}

TEST_CASE
( "A collection of books reports via the book-specific operator<<()" )
{
    std
::vector<ns::book> less = { atocpp }, more = { tcpppl };

    REQUIRE
( less == more );
}

// set CATCH_HOME=D:\Users\Martin\Documents\00_MJM\00_ACCU\PhilNash\Catch\single_include

// cl -nologo -W3 -EHsc -I%CATCH_HOME% catch_udt.cpp && catch_udt
// g++ -Wall -Wextra -Wmissing-include-dirs -std=c++11 -I%CATCH_HOME% -o catch_udt.exe catch_udt.cpp && catch_udt



Martin Moene

unread,
Sep 17, 2014, 4:27:44 PM9/17/14
to catch...@googlegroups.com
Ok, it should be done via a toString( udt const & ) method:

namespace Catch {

std
::ostream & operator<<( std::ostream & os, ::ns::book const & b )
{
   
return os << "[book: " << toString(b.author) << ", " << toString(b.title) << ", " << toString(b.isbn) << "]";
}


std
::string toString( ::ns::book const & b )
{
    std
::stringstream os; os << b; return os.str();
}

} // namespace Catch



Phil Nash

unread,
Sep 18, 2014, 1:13:01 PM9/18/14
to catch...@googlegroups.com
The first version should have worked! Catch::toString() falls back to << for std::ostream& if there is no overload specific to your type.
You also shouldn't need it to be in the Catch namespace - just visible to anything in Catch or in your own type's namespace (assuming you have an ADL capable compiler).

Phil Nash

unread,
Sep 18, 2014, 1:21:11 PM9/18/14
to catch...@googlegroups.com
I can confirm that moving the operator << overload into the type's namespace makes it start working.

The way you'd usually decide whether to write operator << or Catch::toString is whether the conversion is purely for testing (and will be local to your test project) or whether it makes sense for your type in general (and is local to your type).

Martin Moene

unread,
Sep 18, 2014, 3:58:15 PM9/18/14
to catch...@googlegroups.com
Thanks Phil,

Also tried it with success. So a user-defined type stream operator may look like the code below.

Perhaps it's useful to document how to report a user-defined type together with your note when to use toString or operator<<(), even though it has not been frequently asked ;)

cheers,
Martin

namespace ns {

struct book { ... };

std
::ostream & operator<<( std::ostream & os, book const & b )
{
   
using Catch::toString;

   
return os << "[book: " << toString(b.author) << ", " << toString(b.title) << ", " << toString(b.isbn) << "]";
}

} // namespace ns


Phil Nash

unread,
Sep 18, 2014, 4:08:25 PM9/18/14
to catch...@googlegroups.com

Perhaps it's useful to document how to report a user-defined type together with your note when to use toString or operator<<(), even though it has not been frequently asked ;)


I've actually had some docs for toString et al on the workbench for a while - but I've held back on it as toString needs some (possibly interface breaking) work to make it robust. I'm in an email exchange with Andy Sawyer at the moment discussing the nuances of it - hope to have it all complete soon (any changes will go on the develop branch). Then I'll finalise and add those docs.

Phil Nash

unread,
Dec 12, 2014, 3:30:53 AM12/12/14
to catch...@googlegroups.com
FYI those docs are in now:



I didn't change toString itself much in the end, so this is pretty much what I already had when I posted before.
Do let me know if it makes sense and covers what needs to be known.

Martin Moene

unread,
Dec 12, 2014, 4:11:58 AM12/12/14
to Phil Nash, catch...@googlegroups.com
Would it be helpful to explicitly mention that
std::ostream& operator << ( std::ostream& os, T const& value );
is in your own (T's) namespace, even though use of T implies it?

(Occasionally I encounter such functions put in the std namespace, or in Catch's namespace (me;) )

Phil Nash

unread,
Dec 12, 2014, 1:49:51 PM12/12/14
to catch...@googlegroups.com, pan.e...@gmail.com
Does the line:

"You should put this function in the same namespace as your type"

not cover it?

Martin Moene

unread,
Dec 12, 2014, 2:08:35 PM12/12/14
to Phil Nash, catch...@googlegroups.com
Yes indeed, that does not not cover it completely!

Wow, it has been there all along...

Sorry for the noise!

Phil Nash

unread,
Dec 12, 2014, 5:49:08 PM12/12/14
to catch...@googlegroups.com, pan.e...@gmail.com
I presume you mean, "that does cover it completely"?

Either way - no worries. Thanks for the feedback. It was well considered, even if unnecessary :-)

Martin Moene

unread,
Dec 12, 2014, 6:16:41 PM12/12/14
to Phil Nash, catch...@googlegroups.com
On 12-Dec-14 23:49, Phil Nash wrote:
I presume you mean, "that does cover it completely"?

Indeed I do and it does.


Either way - no worries. Thanks for the feedback. It was well considered, even if unnecessary :-)

a kind of "failure demand" ;)

Reply all
Reply to author
Forward
0 new messages