Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overload operator>> for std::pair<int, int>

I am trying to use boost::lexical_cast on a std::pair<int, int>.

#include <iostream>
#include <utility>
#include <boost/lexical_cast.hpp>

namespace my
{
  // When my_pair is a user defined type, this program compiles
  // and runs without any problems.
  // When declaring my_pair as an alias of std::pair<int, int>,
  // it fails to compile

  /*
  struct my_pair
  {
      int first;
      int second;
  };
  */

  using my_pair = std::pair<int, int>;

  std::istream& operator>>(std::istream& stream, my_pair& pair)
  {
    stream >> pair.first;
    stream >> std::skipws;
    stream >> pair.second;
    return stream;
  }
}

int main()
{
  my::my_pair p = boost::lexical_cast<my::my_pair>("10 10");
  std::cout << p.first << " " << p.second << std::endl;
  return 0;
}

If I understand right, in order to make ADL work, the operator>> has to be in the same namespace as my_pair, so std.

Doing so, would result in undefined behavior, because I would be adding functions to the namespace std.

I would like to avoid inheritance, as in struct my_pair : std::pair<int, int>.

What could be the solution to this problem?

I am using clang++-3.6 on OS X.

like image 953
thegameg Avatar asked Jun 04 '15 11:06

thegameg


3 Answers

Instead of ADL-hooking on the value to stream, you could overload on the stream (tagging it in some way):

int main() {
    std::map<int, std::string> standback { { 42, "I'm gonna try" }, { 1729, "science" } };

    streaming::tag_ostream out = std::cout;

    for (auto& entry : standback)
        out << entry << "\n";
}

This way, you can ADL-hook on a namespace that is under your control. You can make the tagging more generic (think auto out = streaming::tag(std::cout)).

Now, a simple implementation of that could look like

namespace streaming {

    template <typename T>
    struct tag : std::reference_wrapper<T> {
        using std::reference_wrapper<T>::reference_wrapper;
    };

    using tag_ostream = tag<std::ostream>;

    template <typename T1, typename T2>
    static inline tag_ostream operator<<(tag_ostream os, std::pair<T1, T2> const& p) {
        os.get() << "std::pair{" << p.first << ", " << p.second  << "}";
        return os;
    }

    template <typename Other>
    static inline tag_ostream operator<<(tag_ostream os, Other const& o) {
        os.get() << o;
        return os;
    }
}

See it Live On Coliru, which prints:

std::pair{42, I'm gonna try}
std::pair{1729, science}
like image 185
sehe Avatar answered Oct 16 '22 13:10

sehe


I know you said that you didn't want this, but I definitely would use inheritance:

#include <iostream>
#include <utility>
#include <boost/lexical_cast.hpp>

namespace my
{
   struct my_pair : std::pair<int, int> {};

   std::istream& operator>>(std::istream& stream, my_pair& pair)
   {
      stream >> pair.first;
      stream >> std::skipws;
      stream >> pair.second;
      return stream;
   }
}

int main()
{
    my::my_pair p = boost::lexical_cast<my::my_pair>("10 10");
    std::cout << p.first << " " << p.second << std::endl;
}

(live demo)

Your my::my_pair literally is-a std::pair<int, int>; you just need it to be a distinct type in your own namespace. This is what inheritance is for.

I'm just leaving this here to show how easy it is to do and explain why I think you should do it.

like image 2
Lightness Races in Orbit Avatar answered Oct 16 '22 15:10

Lightness Races in Orbit


Doing so, would result in undefined behavior, because I would be adding functions to the namespace std.

I would like to avoid inheritance, as in struct my_pair : std::pair.

I wanted to say "inheritance", but you dismiss it ...

You could use encapsulation, simply adding another strong type over std::pair<int,int> (but in such a trivial case, you may be better off with the custom structure - your commented code):

struct my_pair
{
    std::pair<int,int> value;
    // TODO: add any access interface here
};

std::istream& operator>>(std::istream& stream, my_pair& pair)
{
    stream >> pair.value.first;
    stream >> std::skipws;
    stream >> pair.value.second;
    return stream;
}

Infact, you probably should do this, because std::pair is more of a building block, not something that should be used to represent semantic information (and not something that should be dirrectly printable to a stream).

like image 2
utnapistim Avatar answered Oct 16 '22 15:10

utnapistim