[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c++

cout << vector

barcaroller

11/6/2008 11:07:00 PM


In the boost::program_options tutorial, the author included the following
code:


cout << "Input files are: "
<< vm["input-file"].as< vector<string> >()
<< "\n";


Basically, he is trying to print a vector of string, in one line. I could
not get this compile (let alone run). To get it to compile and run, I had
to re-write it as:


vector<string> v = vm["input-file"].as< vector<string> >();

vector<string>::const_iterator i;

for ( i = v.begin();
i != v.end();
++i )
{
cout << *citer << " ";
}


Does anyone know if the original line of code is correct? If so, what was I
missing?





68 Answers

Maxim Yegorushkin

11/7/2008 11:03:00 AM

0

On Nov 6, 11:06 pm, "barcaroller" <barcarol...@music.net> wrote:
> In the boost::program_options tutorial, the author included the following
> code:
>
>     cout << "Input files are: "
>          << vm["input-file"].as< vector<string> >()
>          << "\n";
>
> Basically, he is trying to print a vector of string, in one line.  I could
> not get this compile (let alone run).  To get it to compile and run, I had
> to re-write it as:
>
>     vector<string> v = vm["input-file"].as< vector<string> >();
>
>     vector<string>::const_iterator i;
>
>     for (   i  = v.begin();
>             i != v.end();
>           ++i )
>     {
>         cout << *citer << " ";
>     }
>
> Does anyone know if the original line of code is correct?  If so, what was I
> missing?

The example probably assumes there is an overloaded operator<<() for
std::ostream and std::vector<>, something like this:

#include <ostream>
#include <iterator>
#include <algorithm>

namespace std {

template<class A1, class A2>
ostream& operator<<(ostream& s, vector<A1, A2> const& vec)
{
copy(vec.begin(), vec.end(), ostream_iterator<A1>(s, "
"));
return s;
}

}

--
Max

Pete Becker

11/7/2008 12:16:00 PM

0

On 2008-11-06 18:06:47 -0500, "barcaroller" <barcaroller@music.net> said:

>
> In the boost::program_options tutorial, the author included the following
> code:
>
>
> cout << "Input files are: "
> << vm["input-file"].as< vector<string> >()
> << "\n";
>
>
>
> Does anyone know if the original line of code is correct?

Not in standard C++.

> If so, what was I
> missing?

Either some code that was mentioned somewhere in the tutorial, or a
better tutorial.

--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)

Pete Becker

11/7/2008 12:18:00 PM

0

On 2008-11-07 06:03:15 -0500, Maxim Yegorushkin
<maxim.yegorushkin@gmail.com> said:

>
> The example probably assumes there is an overloaded operator<<() for
> std::ostream and std::vector<>, something like this:
>
> #include <ostream>
> #include <iterator>
> #include <algorithm>
>
> namespace std {
>
> template<class A1, class A2>
> ostream& operator<<(ostream& s, vector<A1, A2> const& vec)
> {
> copy(vec.begin(), vec.end(), ostream_iterator<A1>(s, "
> "));
> return s;
> }
>
> }

Which has undefined behavior. You can only add template specializations
to namespace std when they depend on user-defined types.

--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)

Jeff Schwab

11/7/2008 12:33:00 PM

0

Pete Becker wrote:
> On 2008-11-07 06:03:15 -0500, Maxim Yegorushkin
> <maxim.yegorushkin@gmail.com> said:

>> The example probably assumes there is an overloaded operator<<() for
>> std::ostream and std::vector<>, something like this:

>> namespace std {
>>
>> template<class A1, class A2>
>> ostream& operator<<(ostream& s, vector<A1, A2> const& vec)

> Which has undefined behavior. You can only add template specializations
> to namespace std when they depend on user-defined types.

The correct alternative, AIUI:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>

template<typename T>
std::ostream& operator<<(std::ostream& out, std::vector<T> const& v) {
if (!v.empty()) {
typedef std::ostream_iterator<T> out_iter;
copy(v.begin(), v.end() - 1, out_iter( out, " " ));
out << v.back();
}
return out;
}

int main() {
int const ints[] = { 1, 2, 3, 4 };
std::cout << std::vector<int>( ints, ints + 4 ) << '\n';
}

Juha Nieminen

11/7/2008 1:41:00 PM

0

Jeff Schwab wrote:
> template<typename T>
> std::ostream& operator<<(std::ostream& out, std::vector<T> const& v) {
> if (!v.empty()) {
> typedef std::ostream_iterator<T> out_iter;
> copy(v.begin(), v.end() - 1, out_iter( out, " " ));
> out << v.back();
> }
> return out;
> }

Is there some advantage of that code over a shorter and simpler:

template<typename T>
std::ostream& operator<<(std::ostream& out, std::vector<T> const& v) {
for(std::size_t i = 0; i < v.size()-1; ++i)
out << v[i] << " ";
out << v.back();
return out;
}

Kai-Uwe Bux

11/7/2008 2:05:00 PM

0

Juha Nieminen wrote:

> Jeff Schwab wrote:
>> template<typename T>
>> std::ostream& operator<<(std::ostream& out, std::vector<T> const& v) {
>> if (!v.empty()) {
>> typedef std::ostream_iterator<T> out_iter;
>> copy(v.begin(), v.end() - 1, out_iter( out, " " ));
>> out << v.back();
>> }
>> return out;
>> }
>
> Is there some advantage of that code over a shorter and simpler:
>
> template<typename T>
> std::ostream& operator<<(std::ostream& out, std::vector<T> const& v) {
> for(std::size_t i = 0; i < v.size()-1; ++i)

Nit: std::size_t is not guaranteed to be vector::size_type.

> out << v[i] << " ";
> out << v.back();

I think, v.back() has undefined behavior if the v is empty.

> return out;
> }


Best

Kai-Uwe Bux

Jeff Schwab

11/7/2008 2:19:00 PM

0

Juha Nieminen wrote:
> Jeff Schwab wrote:
>> template<typename T>
>> std::ostream& operator<<(std::ostream& out, std::vector<T> const& v) {
>> if (!v.empty()) {
>> typedef std::ostream_iterator<T> out_iter;
>> copy(v.begin(), v.end() - 1, out_iter( out, " " ));
>> out << v.back();
>> }
>> return out;
>> }
>
> Is there some advantage of that code over a shorter and simpler:
>
> template<typename T>
> std::ostream& operator<<(std::ostream& out, std::vector<T> const& v) {
> for(std::size_t i = 0; i < v.size()-1; ++i)
> out << v[i] << " ";
> out << v.back();
> return out;
> }

The use of the standard algorithm (rather than a hand-rolled loop) helps
separate the different levels of abstraction. In your example, the code
that works with an individual datum of type T is all mixed up with the
code that works with the vector as a whole. There are decent
discussions of this in Effective STL (Scott Meyers) and Clean Code
(Robert Martin).

James Kanze

11/7/2008 5:32:00 PM

0

On Nov 7, 1:32 pm, Jeff Schwab <j...@schwabcenter.com> wrote:
> Pete Becker wrote:
> > On 2008-11-07 06:03:15 -0500, Maxim Yegorushkin
> > <maxim.yegorush...@gmail.com> said:
> >> The example probably assumes there is an overloaded operator<<() for
> >> std::ostream and std::vector<>, something like this:
> >> namespace std {
>
> >> template<class A1, class A2>
> >> ostream& operator<<(ostream& s, vector<A1, A2> const& vec)
> > Which has undefined behavior. You can only add template specializations
> > to namespace std when they depend on user-defined types.

> The correct alternative, AIUI:

> #include <algorithm>
> #include <iostream>
> #include <iterator>
> #include <vector>

> template<typename T>
> std::ostream& operator<<(std::ostream& out, std::vector<T> const& v) {
> if (!v.empty()) {
> typedef std::ostream_iterator<T> out_iter;
> copy(v.begin(), v.end() - 1, out_iter( out, " " ));
> out << v.back();
> }
> return out;
> }

> int main() {
> int const ints[] = { 1, 2, 3, 4 };
> std::cout << std::vector<int>( ints, ints + 4 ) << '\n';
> }

Correct in what sense? It's OK for simple test, like this, but
it's certainly not something you'd allow in production code.

--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Jeff Schwab

11/7/2008 6:14:00 PM

0

Jeff Schwab wrote:
> Juha Nieminen wrote:
>> Jeff Schwab wrote:
>>> template<typename T>
>>> std::ostream& operator<<(std::ostream& out, std::vector<T> const& v) {
>>> if (!v.empty()) {
>>> typedef std::ostream_iterator<T> out_iter;
>>> copy(v.begin(), v.end() - 1, out_iter( out, " " ));
>>> out << v.back();
>>> }
>>> return out;
>>> }
>>
>> Is there some advantage of that code over a shorter and simpler:
>>
>> template<typename T>
>> std::ostream& operator<<(std::ostream& out, std::vector<T> const& v) {
>> for(std::size_t i = 0; i < v.size()-1; ++i)
>> out << v[i] << " ";
>> out << v.back();
>> return out;
>> }
>
> The use of the standard algorithm (rather than a hand-rolled loop) helps
> separate the different levels of abstraction. In your example, the code
> that works with an individual datum of type T is all mixed up with the
> code that works with the vector as a whole. There are decent
> discussions of this in Effective STL (Scott Meyers) and Clean Code
> (Robert Martin).

You know, the code I posted has exactly the same problem I pointed out
in yours: the level of abstraction is mixed. I was imagining the work
split neatly between two standard utilities:

1. std::copy handles iteration through the vector.
2. std::ostream_iterator handles the output of each element.

The problem is that the last item should be output slightly differently
from the others, so the code can't be factored quite so neatly with
those utilities. The semantic messiness is reflected in the code: the
explicit check for an empty vector (which should be irrelevant at that
level), the hard-coded -1, and the call of v.back() are all symptoms.
The worst issue is the manual output of the last vector element, which
should be the ostream_iterator's job. You could improve the situation a
little by delegating to another ostream_iterator:

*out_iterator( out ) = v.back();

You would still be doing the copy algorithm's job, though. You could
avoid that by calling copy twice, e.g:

copy(v.begin(), v.end()-1, out_iter( out, " " ));
copy(v.end()-1, v.end(), out_iter( out ));

That's silly, though, because it implies a loop over just one element.
You don't need the full copy algorithm; it's just that std::copy is the
closest thing to what we need that happens to be in the standard library.

What's really needed is a set of utilities that better fit the natural
division of the problem. The following seems to me like a reasonable
solution: Use a custom (but generic) copy algorithm that implicitly
gives the last element special treatment, and use two separate output
iterators:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>

/* Assign all but the last element of [pos, end) to *out, and assign
* the last element to *fin. */

template<typename Fwd_iter, typename Out_iter>
void final_copy(
Fwd_iter pos, Fwd_iter end,
Out_iter out, Out_iter fin) {
if (pos != end) {
for (Fwd_iter next = pos; ++next != end; pos = next) {
*out = *pos;
++out;
}
*fin = *pos;
++fin;
}
}

template<typename T>
std::ostream& operator<<(
std::ostream& out, std::vector<T> const& v) {
typedef std::ostream_iterator<T> out_iter;
final_copy(
v.begin(), v.end(),
out_iter( out, " " ),
out_iter( out ));
return out;
}

int main() {
int const ints[] = { 1, 2, 3, 4 };
std::cout << std::vector<int>( ints, ints + 4 ) << '\n';
}


The output operator for the vector still has two responsibilities:
Configuration/composition of the utilities to perform the actual output,
and implementation of the standard conventions for operator<<, e.g.
returning the output stream by reference. It probably should be further
refactored along those lines:

template<typename T>
void print(std::ostream& out, std::vector<T> const& v) {
typedef std::ostream_iterator<T> out_iter;
final_copy(
v.begin(), v.end(),
out_iter( out, " " ),
out_iter( out ));
}

template<typename T>
std::ostream& operator<<(
std::ostream& out, std::vector<T> const& v) {
print(out, v);
return out;
}

The only thing left that I don't like is the configuration code embedded
in the print function, specifically the hard-coded " ". The best
solution (for non-trivial applications) might be a traits class that
tells what the separator should be between elements, and defaults to " "
or "\n":

out_iterator( out, separator<T>::value );

Jeff Schwab

11/7/2008 6:21:00 PM

0

James Kanze wrote:
> On Nov 7, 1:32 pm, Jeff Schwab <j...@schwabcenter.com> wrote:
>> Pete Becker wrote:
>>> On 2008-11-07 06:03:15 -0500, Maxim Yegorushkin
>>> <maxim.yegorush...@gmail.com> said:
>>>> The example probably assumes there is an overloaded operator<<() for
>>>> std::ostream and std::vector<>, something like this:
>>>> namespace std {
>>>> template<class A1, class A2>
>>>> ostream& operator<<(ostream& s, vector<A1, A2> const& vec)
>>> Which has undefined behavior. You can only add template specializations
>>> to namespace std when they depend on user-defined types.
>
>> The correct alternative, AIUI:
>
>> #include <algorithm>
>> #include <iostream>
>> #include <iterator>
>> #include <vector>
>
>> template<typename T>
>> std::ostream& operator<<(std::ostream& out, std::vector<T> const& v) {
>> if (!v.empty()) {
>> typedef std::ostream_iterator<T> out_iter;
>> copy(v.begin(), v.end() - 1, out_iter( out, " " ));
>> out << v.back();
>> }
>> return out;
>> }
>
>> int main() {
>> int const ints[] = { 1, 2, 3, 4 };
>> std::cout << std::vector<int>( ints, ints + 4 ) << '\n';
>> }
>
> Correct in what sense? It's OK for simple test, like this, but
> it's certainly not something you'd allow in production code.

Correct in the sense that it implements the OP's intent, and is allowed
by the standard. As far as using it in production code... Ad hoc
output of this sort only really comes up for debugging purposes. If you
don't even think it's OK for debug, I'd love to know your suggested
alternative.