std::copy to std::cout for std::pair

18,259

Solution 1

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.

Solution 2

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));

Solution 3

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 + "]";
    }
...

Solution 4

Just passing by, but this did the job for me, so it can for somebody else (cut version):

template<typename First, typename Second>
struct first_of {
    First& operator()(std::pair<First, Second>& v) const {
        return v.first;
    }
};

Use case given:

transform (v.begin (), v.end (), 
           ostream_iterator<int>(cout, "\n"), first_of<int, string> ());

Solution 5

[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) { ... }
Share:
18,259
bayda
Author by

bayda

--

Updated on June 05, 2022

Comments

  • bayda
    bayda almost 2 years

    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?