How to read arbitrary number of values using std::copy?
Asked Answered
M

8

8

I'm trying to code opposite action to this:

std::ostream outs; // properly initialized of course
std::set<int> my_set; // ditto

outs << my_set.size();
std::copy( my_set.begin(), my_set.end(), std::ostream_iterator<int>( outs ) );

it should be something like this:

std::istream ins;

std::set<int>::size_type size;
ins >> size;

std::copy( std::istream_iterator<int>( ins ), std::istream_iterator<int>( ins ) ???, std::inserter( my_set, my_set.end() ) );

But I'm stuck with the 'end' iterator -- input interators can't use std::advance and neither I can use two streams with the same source...

Is there any elegant way how to solve this? Of course I can use for loop, but maybe there's something nicer :)

Matchboard answered 30/10, 2008 at 13:2 Comment(0)
Q
3

You could derive from the istream_iterator<T>.
Though using Daemin generator method is another option, though I would generate directly into the set rather than use an intermediate vector.

#include <set>
#include <iterator>
#include <algorithm>
#include <iostream>


template<typename T>
struct CountIter: public std::istream_iterator<T>
{
    CountIter(size_t c)
        :std::istream_iterator<T>()
        ,count(c)
    {}
    CountIter(std::istream& str)
        :std::istream_iterator<T>(str)
        ,count(0)
    {}

    bool operator!=(CountIter const& rhs) const
    {
        return (count != rhs.count) && (dynamic_cast<std::istream_iterator<T> const&>(*this) != rhs);
    }
    T operator*()
    {
        ++count;
        return std::istream_iterator<T>::operator*();
    }

    private:
        size_t  count;
};

int main()
{
    std::set<int>       x;

    //std::copy(std::istream_iterator<int>(std::cin),std::istream_iterator<int>(),std::inserter(x,x.end()));
    std::copy(
                CountIter<int>(std::cin),
                CountIter<int>(5),
                std::inserter(x,x.end())
            );
}
Qua answered 30/10, 2008 at 16:3 Comment(3)
while this is a very cool solution, it should be noted that simply writing a loop and not doing this would be shorter.Alvera
You've used dynamic_cast on nonpolymorphic type. This shouldn't compileConfessional
@Armen Tsirunyan: Your argument is self defeating. You are correct in your assertion that non-polymorphics types will not compile when used with dynamic_cast. Yet the above does compile and works correctly. Thus by occams razer we are left with the conclusion that it is a polymorphic type. Thanks you and good night. :-)Qua
N
3

Use:

std::copy( std::istream_iterator<int>(ins),
           std::istream_iterator<int>(),
           std::inserter(my_set, my_set.end())
         );

Note the empty parameter:

std::istream_iterator<int>();
Narbonne answered 11/12, 2008 at 18:19 Comment(2)
Damn it, thanks! I could swear I tried this but as it seems I didn't. It works great, many thanks! (side note: it's always great to look after own posts :)Matchboard
On the other hand, how could one define for example to read three bytes, not whole file? ;) So this was my problem with accepting your answer back then, now I remember.Matchboard
P
2

Errr... copy_n() algorithm?

Proteiform answered 30/10, 2008 at 13:53 Comment(1)
"This function is an SGI extension; it is not part of the C++ standard." And this is very true, it's missing in VS2008 includes at least. But nice one, I nearly wanted to blame myself how it's possible I overlooked it ;)Matchboard
C
2

Looking into this a bit I don't think reading directly into a set will work, as you need to call insert on it to actually add the elements (I could be mistaken, it is rather early in the morning here). Though looking at the STL documentation in VS2005 briefly I think something using the generate_n function should work, for instance:

std::istream ins;
std::set<int> my_set;
std::vector<int> my_vec;

struct read_functor
{
    read_functor(std::istream& stream) :
        m_stream(stream)
    {
    }

    int operator()
    {
        int temp;
        m_stream >> temp;
        return temp;
    }
private:
    std::istream& m_stream;
};

std::set<int>::size_type size;
ins >> size;
my_vec.reserve(size);

std::generate_n(my_vec.begin(), size, read_functor(ins));
my_set.insert(my_vec.begin(), my_vec.end());

Hopefully that's either solved your problem, or convinced you that the loop isn't that bad in the grand scheme of things.

Corvin answered 30/10, 2008 at 14:38 Comment(2)
Why use a vector as an intermediate? Just use the generator to insert into the set (using std::inserter).Qua
It was late when I composed the answer, and it did not come to mind at the time. I guess you would use an inserter, though it still requires creation of a whole other class, so for me a loop would be simpler and probbably look better too.Corvin
G
1

How about using an alternate iterator to do the traversal and then use a function object (or lambda) to fill in the container?

istream ins;
set<int>::size_type size;
set<int> new_set;
ins >> size;
ostream_iterator<int> ins_iter(ins);

for_each(counting_iterator<int>(0), counting_iterator<int>(size),
  [&new_set, &ins_iter](int n) { new_set.insert(*ins_iter++); }
);

Of course this assumes you have a C++0x compliant compiler.

BTW, 'counting_iterator<>' is part of Boost.Iterator.

Gothar answered 31/10, 2008 at 7:52 Comment(0)
A
1

Or you could do this:

my_set.insert(std::istream_iterator<int>(ins), std::istream_iterator<int>());
Assimilate answered 6/4, 2011 at 16:1 Comment(0)
H
0

(Edited: I should have read the question closer...)

While somewhat suspect, you can get approximately the right behavior by having an entry in the file that will "fail" the first loop, then clear the fail bit on the stream and start reading more.

Data, without an explicit size, but like this

1 1 2 3 5 8 Fibb

Fed to the code below seems to do what I meant, at least on VS2005 with STLPort.

typedef std::istream_iterator < int, char, std::char_traits ,ptrdiff_t> is_iter;
std::copy( is_iter(cin), is_iter(), inserter(my_set,my_set.end()));
cin.clear();
std::cin >> instr;
Holifield answered 30/10, 2008 at 13:25 Comment(1)
Hehe, you're right, it's kind of suspect and the most important -- it will not work on binary stream :)Matchboard
M
0

Yes sdg but when I want to use another data structures in that file / stream? I should probably explicitly write here, I want to store another stuff after this set, this is the reason why I'm storing the size as well.

Matchboard answered 30/10, 2008 at 13:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.