views:

1569

answers:

6

I have next code:

#include <iostream>
#include <algorithm>
#include <map>
#include <iterator>

//namespace std
//{

std::ostream& operator << ( std::ostream& out, 
       const std::pair< size_t, size_t >& rhs )
{
    out << rhs.first << ", " << rhs.second;
    return out;
}
//}

int main() 
{

    std::map < size_t, size_t > some_map;

    // fill  some_map with random values
    for ( size_t i = 0; i < 10; ++i )
    {
        some_map[ rand() % 10 ] = rand() % 100;
    }

    // now I want to output this map
    std::copy( 
        some_map.begin(), 
        some_map.end(), 
        std::ostream_iterator< 
              std::pair< size_t, size_t > >( std::cout, "\n" ) );

    return 0;
}

In this code I just want copy map to output stream. For do this I need define operator <<(..) - OK. But according names finding rules compiler can't find my operator<<().
Because std::cout, std::pair and std::copy which called my operator<< - all from namespace std.

Quick solution - add my oerator<< to std namespace - but it is ugly, imho.

What solutions or workaround for this problem do you know?

+8  A: 

There is no standard way to cout a std::pair because, well, how you want it printed is probably different from the way the next guy wants it. This is a good use case for a custom functor or a lambda function. You can then pass that as an argument to std::for_each to do the work.

typedef std::map<size_t, size_t> MyMap;

template <class T>
struct PrintMyMap : public std::unary_function<T, void>
{
    std::ostream& os;
    PrintMyMap(std::ostream& strm) : os(strm) {}

    void operator()(const T& elem) const
    {
        os << elem.first << ", " << elem.second << "\n";
    }
}

To call this functor from your code:

std::for_each(some_map.begin(),
              some_map.end(),
              PrintMyMap<MyMap::value_type>(std::cout));
Kristo
+2  A: 

[I'd rather delete this answer, but I'll leave it for now, in case someone finds the discussion interesting.]

Since it's a reasonable extension to the std library, I'd just put it in std namespace, especially if this is a one time thing. You can just declare it static to prevent it from causing linker errors, should someone else do the same thing someplace else.

Another solution that comes to mind is to create a wrapper for std::pair:

template<class A, class B>
struct pairWrapper {
  const std::pair<A,B> & x;
  pairWrapper(const std::pair<A,B> & x) : x(x) {}
}

template<class A,class B>
std::ostream & operator<<(std::ostream & stream, const pairWrapper<A,B> & pw) { ... }
TrayMan
j_random_hacker
Nice, thank you.
bb
Wrong, disallowed. Namespace std is for compiler-provided classes, templates and functions. You may not add overloads.
MSalters
You'll probably need `operator=` for your pairWrapper as well because std::ostream_iterator requiers it and a compiler wouldn't be able to generate it for you due to a const member.
Serge
@Serge: Are you sure? Because that would mean it can't print immutable objects at all. Maybe you mean the copy constructor?
TrayMan
@j_random_hacker: I didn't even think about the automatic conversion!
TrayMan
@Serge: Actually it's not a bad idea to leave operator=() out and thereby force a compiler error if it is called, as that makes it harder to misuse the class -- it should never be assigned in normal operation (though it may be copy-constructed).
j_random_hacker
@TrayMan, I wasn't quite right about 'operator=', see 'Note' here http://www.sgi.com/tech/stl/ostream_iterator.html
Serge
Hmm. After failing to get this or simpler variants to compile, I realised: user-defined conversions are not tried during template argument deduction. I.e. when trying to find a match to the 2nd param of your templated operator<<(), C++ won't consider the implicit conversion from pair<>. Damn!
j_random_hacker
@jrh: I feared that might be the case. Now I also realize that I didn't fully read the question and my soluton isn't very useful at all. If for_each is undesirable, working from MSalters's idea is probably the best option, otherwise Kristo's.
TrayMan
+6  A: 

I'd just like to point out that adding things to the std:: namespace is illegal according to the C++ Standard (see section 17.4.3.1).

anon
Important remark, thank you.
bb
One exception: you may add overloads for `std::swap`.
Konrad Rudolph
@konrad go you have a reference for that?
anon
@konrad s/go/do/
anon
Herb Sutter, in one of his Gurus of the Week, suggested "using std::swap" at the start of each file, so that standard swaps and user-defined swaps would use the same syntax.
David Thornley
@david not quite the same as adding an overload to the std:: namespace though, I think?
anon
@Neil: Sorry, no standard reference. Scott Meyers says so in Eff C++ in his item on implementing exception-safe `swap` though. Due to a move all my books are packed away so I can't look up the exact passage. :-(
Konrad Rudolph
+3  A: 

What you want is a transforming iterator. This kind of iterator wraps another iterator, forwards all positioning methods like operator++ and operator==, but redefines operator* and operator->.

Quick sketch :

template <typename ITER> 
struct transformingIterator : private ITER {
    transformingIterator(ITER const& base) : ITER(base) {}
    transformingIterator& operator++() { ITER::operator++(); return *this; }
    std::string operator*() const
    {
        ITER::value_type const& v = ITER::operator*();
        return "[" + v->first +", " + v->second + "]";
    }
...
MSalters
Thanks. Nice idea to create iterator wrappers, this idea could be generalized and used for solve other problems.
bb
If you want something generic, one obvious step is to store the transformation in an appropriate boost::function. You'd need an extra template parameter for the new value_type, of course.
MSalters
+2  A: 

Using Boost Lambda, you could try something like this. The version I have of Boost Lambda, this doesn't actually work, I'll test and fix later.

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

using namespace boost::lambda;

std::for_each( some_map.begin(), some_map.end(), 
               std::cout << bind( &std::map<size_t,size_t>::value_type::first, _1 )
                         << ","
                         << bind( &std::map<size_t,size_t>::value_type::second, _1 ) );
ceretullis
+1  A: 

I've founded one new elegant way to solve this problem.
I've got many interest ideas when read answers:

  • wrap iterator, for transform std::pair to std::string;
  • wrap std::pair, for have a chance to overload operator<<(...);
  • use usual std::for_each with printing functor;
  • use std::for_each with boost::labda - looks nice, except accessing to std::pair< >::first and std::pair< >::second members;

I think I will use all of this ideas in future for solve different other problems.
But for this case I've understaded that I can formulate my bproblem as "transform map's data to strings and write them to output stream" instead "copy map's data to ouput stream". My solution looks like:

namespace
{
std::string toString( const std::pair< size_t, size_t >& data)
{
    std::ostringstream str;
    str << data.first << ", " << data.second;
    return str.str();
}
} // namespace anonymous

std::transform( 
    some_map.begin(), 
    some_map.end(), 
    std::ostream_iterator< std::string >( std::cout, "\n" ),
    toString );

I think this method is most short and expressive than others.

bb