reading and writing a container of std::pair type

  • Thread starter V.Subramanian, India
  • Start date
V

V.Subramanian, India

Suppose I have a container, say vector, of element type
pair<int, string>. I want to populate the container with some values
through istream_iterator. Then I want to write back the values to cout/
file/ostringstream objects.

Consider the following program x.cpp:

#include <cstdlib>
#include <iostream>
#include <ostream>
#include <vector>
#include <string>
#include <utility>
#include <algorithm>
#include <iterator>

using namespace std;

typedef pair<int, string> pair_type;

inline void print(const pair_type& arg)
{
cout << arg.first << " " << arg.second << endl;
}

inline istream& operator>>(istream& in,
pair_type& arg)
{
int x;
string str;

if (in >> x >> str)
{
arg.first = x;
arg.second = str;
}

return in;
}

inline ostream& operator<<(ostream& out,
const pair_type& arg)
{
return out << arg.first << " " << arg.second << endl;
}

int main()
{
istream_iterator<pair_type> ii(cin);
istream_iterator<pair_type> eos;
vector<pair_type> v(ii, eos);

for_each(v.begin(), v.end(), print);

cout << "-----------------------------" << endl;

copy(v.begin(),
v.end(),
ostream_iterator<pair_type>(cout));

return EXIT_SUCCESS;
}

I compiled this program as
g++ -std=c++98 -pedantic -Wall -Wextra x.cpp
Even though I have provided overloaded input and output operators for
the 'pair_type', I get compilation error for populating the vector
through istream_iterator and copying through ostream_iterator. Since
the compilation error messages are so huge that I am unable to put
them here.

As expected there is no compilation error for the 'std::for_each()'
algorithm; but I do not want to use it to write values because 'cout'
is hard-wired into it. If I can somehow use 'std::copy()' algorithm,
then I can pass cout/ofstream/ostringstream objects to std::copy()
through ostream_iterator. Am I correct ?

Why doesn't the above program compile?

Suppose 'T' and 'U' are any two types. Suppose I have a container of
std::pair<T, U>. Then what is done in real-world code to populate the
container(especially read each pair<T, U>) and write them to any
ostream/ofstream/ostringstream objects? Please provide the code. I am
asking the code for two reasons:1) my code doesn't compile. 2) I want
to know the clean solution used in real-world code.

Thanks
V.Subramanian
 
A

AnonMail2005

Suppose I have a container, say vector, of element type
pair<int, string>. I want to populate the container with some values
through istream_iterator. Then I want to write back the values to cout/
file/ostringstream objects.

Consider the following program x.cpp:

#include <cstdlib>
#include <iostream>
#include <ostream>
#include <vector>
#include <string>
#include <utility>
#include <algorithm>
#include <iterator>

using namespace std;

typedef pair<int, string> pair_type;

inline void print(const pair_type& arg)
{
    cout << arg.first << "  " << arg.second << endl;

}

inline istream& operator>>(istream& in,
                                       pair_type& arg)
{
    int x;
    string str;

    if (in >> x >> str)
    {
        arg.first = x;
        arg.second = str;
    }

    return in;

}

inline ostream& operator<<(ostream& out,
                                         const pair_type& arg)
{
    return out << arg.first << "  " << arg.second << endl;

}

int main()
{
    istream_iterator<pair_type> ii(cin);
    istream_iterator<pair_type> eos;
    vector<pair_type> v(ii, eos);

    for_each(v.begin(), v.end(), print);

    cout << "-----------------------------" << endl;

    copy(v.begin(),
            v.end(),
            ostream_iterator<pair_type>(cout));

    return EXIT_SUCCESS;

}

I compiled this program as
g++ -std=c++98 -pedantic -Wall -Wextra x.cpp
Even though I have provided overloaded input and output operators for
the 'pair_type', I get compilation error for populating the vector
through istream_iterator and copying through ostream_iterator. Since
the compilation error messages are so huge that I am unable to put
them here.

As expected there is no compilation error for the 'std::for_each()'
algorithm; but I do not want to use it to write values because 'cout'
is hard-wired into it. If I can somehow use 'std::copy()' algorithm,
then I can pass cout/ofstream/ostringstream objects to std::copy()
through ostream_iterator. Am I correct ?

Why doesn't the above program compile?

Suppose 'T' and 'U' are any two types. Suppose I have a container of
std::pair<T, U>. Then what is done in real-world code to populate the
container(especially read each pair<T, U>) and write them to any
ostream/ofstream/ostringstream objects? Please provide the code. I am
asking the code for two reasons:1) my code doesn't compile. 2) I want
to know the clean solution used in real-world code.

Thanks
V.Subramanian

These statements look suspect:
istream_iterator<pair_type> ii(cin);
istream_iterator<pair_type> eos;
vector<pair_type> v(ii, eos);

I don't think you can initialize a vector this way. Reason being, you
need an iterator pointing to the end of the stream. Not sure how you
can do this with an istream. In your code above, you are default
constructing eos which doesn't seem correct.

HTH
 
J

Joshua Maurice

Suppose I have a container, say vector, of element type
pair<int, string>. I want to populate the container with some values
through istream_iterator. Then I want to write back the values to cout/
file/ostringstream objects.

Consider the following program x.cpp:

#include <cstdlib>
#include <iostream>
#include <ostream>
#include <vector>
#include <string>
#include <utility>
#include <algorithm>
#include <iterator>

using namespace std;

typedef pair<int, string> pair_type;

inline void print(const pair_type& arg)
{
    cout << arg.first << "  " << arg.second << endl;

}

inline istream& operator>>(istream& in,
                                       pair_type& arg)
{
    int x;
    string str;

    if (in >> x >> str)
    {
        arg.first = x;
        arg.second = str;
    }

    return in;

}

inline ostream& operator<<(ostream& out,
                                         const pair_type& arg)
{
    return out << arg.first << "  " << arg.second << endl;

}

int main()
{
    istream_iterator<pair_type> ii(cin);
    istream_iterator<pair_type> eos;
    vector<pair_type> v(ii, eos);

    for_each(v.begin(), v.end(), print);

    cout << "-----------------------------" << endl;

    copy(v.begin(),
            v.end(),
            ostream_iterator<pair_type>(cout));

    return EXIT_SUCCESS;

}

I compiled this program as
g++ -std=c++98 -pedantic -Wall -Wextra x.cpp
Even though I have provided overloaded input and output operators for
the 'pair_type', I get compilation error for populating the vector
through istream_iterator and copying through ostream_iterator. Since
the compilation error messages are so huge that I am unable to put
them here.

As expected there is no compilation error for the 'std::for_each()'
algorithm; but I do not want to use it to write values because 'cout'
is hard-wired into it. If I can somehow use 'std::copy()' algorithm,
then I can pass cout/ofstream/ostringstream objects to std::copy()
through ostream_iterator. Am I correct ?

Why doesn't the above program compile?

Suppose 'T' and 'U' are any two types. Suppose I have a container of
std::pair<T, U>. Then what is done in real-world code to populate the
container(especially read each pair<T, U>) and write them to any
ostream/ofstream/ostringstream objects? Please provide the code. I am
asking the code for two reasons:1) my code doesn't compile. 2) I want
to know the clean solution used in real-world code.

Interesting. I'm sorry that I can't pinpoint exactly what's wrong and
what to fix it, but I think it has something to do with template's two
phase lookup and argument dependent lookup (ADL) aka Koenig lookup.

If you change pair_type from a typedef of a std:: type to a class that
contains a std::pair, then it works. IIRC, in the code of
istream_iterator, the use of operator>> is operating on a type-
dependent name, and that should be delayed to phase 2 lookup, right?
And I thought that second phase lookup uses the names in the scope of
the instantiation, and thus it should be able to find your global
operator>> function. Obviously my reasoning is mistaken somewhere. I'm
not quite sure where.

(At least, http://www.comeaucomputing.com/tryitout/ says it's broken,
and Comeau is usually right.)
 
G

Gil

Consider the following program x.cpp:

using namespace std;
typedef pair<int, string> pair_type;

7.1.3 [dcl.typedef] /1 "A typedef-name is thus a synonym for another
type.
A typedef-name does not introduce a new type"[...]

your pair_type is a std:: namespace type name.
int main()
{
    istream_iterator<pair_type> ii(cin);

24.6.1.1 [istream.iterator.cons] /3
istream_iterator(istream_type& s);
3 Effects: Initializes in_stream with &s.
value may be initialized during construction or the first time
it is referenced.[...]

inplementations are allowed (but not forced) to use extractor
in initialization. when implementation evaluates expression

*in_stream >> value

it does it in a context of an argument dependent call in the scope
of std::istream_iterator thus it will search first the most inner
scope for a suitable operator >>.
none are found.
then it does 3.4.2 [basic.lookup.argdep] but associated namespaces
for both arguments are namespace std:: as well.
your user defined operator in global scope is never found.

note: it should be found if you do something like

pair_type myp;
cin >> myp;

through ordinary lookup.

the key to understand the issue is pair_type is a std:: type and
it is not associated with your operator's scope.

3.4.2 [basic.lookup.argdep] /2
[...]Typedef names and using-declarations used to specify the types
do not contribute to this set.[...]

the clean solution imo is to use your own data type instead of
std::pair.

hth,
gil
 
V

V.Subramanian, India

Consider the following program x.cpp:
using namespace std;
typedef pair<int, string> pair_type;

7.1.3 [dcl.typedef] /1 "A typedef-name is thus a synonym for another
type.
A typedef-name does not introduce a new type"[...]

your pair_type is a std:: namespace type name.


int main()
{
istream_iterator<pair_type> ii(cin);

24.6.1.1 [istream.iterator.cons] /3
istream_iterator(istream_type& s);
3 Effects: Initializes in_stream with &s.
value may be initialized during construction or the first time
it is referenced.[...]

inplementations are allowed (but not forced) to use extractor
in initialization. when implementation evaluates expression

*in_stream >> value

it does it in a context of an argument dependent call in the scope
of std::istream_iterator thus it will search first the most inner
scope for a suitable operator >>.
none are found.
then it does 3.4.2 [basic.lookup.argdep] but associated namespaces
for both arguments are namespace std:: as well.
your user defined operator in global scope is never found.

note: it should be found if you do something like

pair_type myp;
cin >> myp;

through ordinary lookup.

the key to understand the issue is pair_type is a std:: type and
it is not associated with your operator's scope.

3.4.2 [basic.lookup.argdep] /2
[...]Typedef names and using-declarations used to specify the types
do not contribute to this set.[...]

the clean solution imo is to use your own data type instead of
std::pair.

hth,
gil

Thanks for your help.

I checked the ISO C++ Standard document for the section
3.4.2. But I am unable to understand the Standard document.
However I understodd from your reply that my operator<<
and operator>> are not considered for the reasons stated by
you. Also as advised by you, I have attempted to write a class
wrapping pair<T, U>. The program compiles fine and
works as expected. Here is the program x.cpp:

#include <cstdlib>
#include <iostream>
#include <ostream>
#include <string>
#include <utility>
#include <iterator>
#include <vector>
#include <algorithm>
#include <list>

using namespace std;

template <typename T, typename U>
class ExtractorInsertor {
public:
ExtractorInsertor(const pair<T, U>& arg =
make_pair(T(), U()));
ExtractorInsertor(const ExtractorInsertor& rhs);
ExtractorInsertor& operator=(
const ExtractorInsertor& rhs);
~ExtractorInsertor();
T first() const;
void first(const T& arg);
U second() const;
void second(const U& arg);
istream& readFrom(istream& in);
ostream& writeTo(ostream& out) const;
private:
pair<T, U> pairVal;
};

template <typename T, typename U>
inline ExtractorInsertor<T, U>::ExtractorInsertor(
const pair<T, U>& arg) : pairVal(arg)
{
}

template <typename T, typename U>
inline ExtractorInsertor<T, U>::ExtractorInsertor(
const ExtractorInsertor& rhs) :
pairVal(rhs.pairVal)
{
}

template <typename T, typename U>
inline ExtractorInsertor<T, U>&
ExtractorInsertor<T, U>::eek:perator=(
const ExtractorInsertor& rhs)
{
if (this != &rhs)
pairVal = rhs.pairVal;

return *this;
}

template <typename T, typename U>
inline ExtractorInsertor<T, U>::~ExtractorInsertor()
{
}

template <typename T, typename U>
inline T ExtractorInsertor<T, U>::first() const
{
return pairVal.first;
}

template <typename T, typename U>
inline void ExtractorInsertor<T, U>::first(
const T& arg)
{
pairVal.first = arg;
return;
}

template <typename T, typename U>
inline U ExtractorInsertor<T, U>::second() const
{
return pairVal.second;
}

template <typename T, typename U>
inline void ExtractorInsertor<T, U>::second(
const U& arg)
{
pairVal.second = arg;
return;
}

template <typename T, typename U>
inline istream& ExtractorInsertor<T, U>::readFrom(
istream& in)
{
T first;
U second;

if (in >> first >> second)
pairVal = make_pair(first, second);

return in;
}

template <typename T, typename U>
inline ostream& ExtractorInsertor<T, U>::writeTo(
ostream& out) const
{
return out << pairVal.first
<< " "
<< pairVal.second
<< endl;
}

template <typename T, typename U>
inline istream& operator>>(
istream& in,
ExtractorInsertor<T, U>& arg)
{
return arg.readFrom(in);
}

template <typename T, typename U>
inline ostream& operator<<(
ostream& out,
const ExtractorInsertor<T, U>& arg)
{
return arg.writeTo(out);
}

int main()
{
typedef ExtractorInsertor<int, string> pairTypeOne;
istream_iterator<pairTypeOne> ii(cin);
istream_iterator<pairTypeOne> eos;
typedef vector<pairTypeOne> Container;
Container v(ii, eos);

cout << "-----------------------------" << endl;

copy(v.begin(),
v.end(),
ostream_iterator<Container::value_type>(
cout));

cout << "-----------------------------" << endl;

typedef ExtractorInsertor<string, double> pairTypeTwo;
istream_iterator<pairTypeTwo> iiAnother(cin);
istream_iterator<pairTypeTwo> eosAnother;
typedef list<pairTypeTwo> ContainerAnother;
ContainerAnother c(iiAnother, eosAnother);

cout << "-----------------------------" << endl;

copy(c.begin(),
c.end(),
ostream_iterator<ContainerAnother::value_type>(
cout));

return EXIT_SUCCESS;
}

Is this solution correct ? The problem with the
templatized version is that if ExtractorInsertor has
pointer argument types, then copy ctor and copy-assignment
void first(const T& arg);
void second(const U& arg);
will not work correctly because they will just assign
the pointer values. Am I correct ? For this scenario, can
I write specialized versions of ExtractorInsertor with the
appropriate pointer argument types and the affected member
functions beging modified suitably ?

Please explain.

Thanks
V.Subramanian
 
V

V.Subramanian, India

* On Feb 20, 8:17 am, "V.Subramanian, India"

Sorry I forgot mention the following line in main():
cin.clear();
This should be present after the first copy() invocation in
main().

Please give your feedback.

Here is the modified complete post:

I checked the ISO C++ Standard document for the section
3.4.2. But I am unable to understand the Standard document.
However I understodd from your reply that my operator<<
and operator>> are not considered for the reasons stated by
you. Also as advised by you, I have attempted to write a class
wrapping pair<T, U>. The program compiles fine and
works as expected. Here is the program x.cpp:

#include <cstdlib>
#include <iostream>
#include <ostream>
#include <string>
#include <utility>
#include <iterator>
#include <vector>
#include <algorithm>
#include <list>

using namespace std;

template <typename T, typename U>
class ExtractorInsertor {
public:
ExtractorInsertor(const pair<T, U>& arg =
make_pair(T(), U()));
ExtractorInsertor(const ExtractorInsertor& rhs);
ExtractorInsertor& operator=(
const ExtractorInsertor& rhs);
~ExtractorInsertor();
T first() const;
void first(const T& arg);
U second() const;
void second(const U& arg);
istream& readFrom(istream& in);
ostream& writeTo(ostream& out) const;
private:
pair<T, U> pairVal;
};

template <typename T, typename U>
inline ExtractorInsertor<T, U>::ExtractorInsertor(
const pair<T, U>& arg) : pairVal(arg)
{
}

template <typename T, typename U>
inline ExtractorInsertor<T, U>::ExtractorInsertor(
const ExtractorInsertor& rhs) :
pairVal(rhs.pairVal)
{
}

template <typename T, typename U>
inline ExtractorInsertor<T, U>&
ExtractorInsertor<T, U>::eek:perator=(
const ExtractorInsertor& rhs)
{
if (this != &rhs)
pairVal = rhs.pairVal;

return *this;
}

template <typename T, typename U>
inline ExtractorInsertor<T, U>::~ExtractorInsertor()
{
}

template <typename T, typename U>
inline T ExtractorInsertor<T, U>::first() const
{
return pairVal.first;
}

template <typename T, typename U>
inline void ExtractorInsertor<T, U>::first(
const T& arg)
{
pairVal.first = arg;
return;
}

template <typename T, typename U>
inline U ExtractorInsertor<T, U>::second() const
{
return pairVal.second;
}

template <typename T, typename U>
inline void ExtractorInsertor<T, U>::second(
const U& arg)
{
pairVal.second = arg;
return;
}

template <typename T, typename U>
inline istream& ExtractorInsertor<T, U>::readFrom(
istream& in)
{
T first;
U second;

if (in >> first >> second)
pairVal = make_pair(first, second);

return in;
}

template <typename T, typename U>
inline ostream& ExtractorInsertor<T, U>::writeTo(
ostream& out) const
{
return out << pairVal.first
<< " "
<< pairVal.second
<< endl;
}

template <typename T, typename U>
inline istream& operator>>(
istream& in,
ExtractorInsertor<T, U>& arg)
{
return arg.readFrom(in);
}

template <typename T, typename U>
inline ostream& operator<<(
ostream& out,
const ExtractorInsertor<T, U>& arg)
{
return arg.writeTo(out);
}

int main()
{
typedef ExtractorInsertor<int, string> pairTypeOne;
istream_iterator<pairTypeOne> ii(cin);
istream_iterator<pairTypeOne> eos;
typedef vector<pairTypeOne> Container;
Container v(ii, eos);

cout << "-----------------------------" << endl;

copy(v.begin(),
v.end(),
ostream_iterator<Container::value_type>(
cout));

cin.clear();

cout << "-----------------------------" << endl;

typedef ExtractorInsertor<string, double> pairTypeTwo;
istream_iterator<pairTypeTwo> iiAnother(cin);
istream_iterator<pairTypeTwo> eosAnother;
typedef list<pairTypeTwo> ContainerAnother;
ContainerAnother c(iiAnother, eosAnother);

cout << "-----------------------------" << endl;

copy(c.begin(),
c.end(),
ostream_iterator<ContainerAnother::value_type>(
cout));

return EXIT_SUCCESS;
}

Is this solution correct ? The problem with the
templatized version is that if ExtractorInsertor has
pointer argument types, then copy ctor and copy-assignment
void first(const T& arg);
void second(const U& arg);
will not work correctly because they will just assign
the pointer values. Am I correct ? For this scenario, can
I write specialized versions of ExtractorInsertor with the
appropriate pointer argument types and the affected member
functions beging modified suitably ?

Please explain.

Thanks
V.Subramanian
 
G

Gil

* On Feb 20, 8:17 am, "V.Subramanian, India"
Is this solution correct ?

that depends on your test suite: did you validate all tests?
how about an input like:

1 "string with spaces"
2 qwe
The problem with the
templatized version is that if ExtractorInsertor has
pointer argument types, then copy ctor and copy-assignment
   void first(const T& arg);
   void second(const U& arg);
will not work correctly because they will just assign
the pointer values. Am I correct ? For this scenario, can
I write specialized versions of ExtractorInsertor with the
appropriate pointer argument types and the affected member
functions beging modified suitably ?

Please explain.

you don't need copy, assign and destructor for your wrapper,
compiler generated are fine. your wrapper also modifies the
semantics of std::pair's first and second.

a wrapper for std::pair is expected to behave like std::pair
does; that should answer your question about pointer values
too.

gil
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top