output_iterator_tag and back_insert_iterator

R

Rares Vernica

Hello,

I am trying to write a generic function that can read data from a file
and store it in a container.

I defined the following function:

template<class T>
void readBinaryFile(
const std::string &filename,
std::iterator<std::eek:utput_iterator_tag, T> data)
{
....
}

Because the function is reading binary data, it needs to know the type of
the elements in the container, T.

When I call it with something like:

vector<float> f;
readBinaryFile<float>("test.bin", back_inserter(f));

I get:

error: no matching function for call to ‘readBinaryFile(std::string&,
std::back_insert_iterator<std::vector<float, std::allocator<float> > >)’

I am a little bit confused because I know that the back_insert_iterator
is an output iterator. In fact, back_insert_iterator is defined as:

template<typename _Container>
class back_insert_iterator
: public iterator<output_iterator_tag, void, void, void, void>

If I change the function declaration to:

template<class T, class OutputIterator>
void readBinaryFile(
const std::string &filename,
OutputIterator data)

it works fine.

If possible, I would prefer to make the first declaration work
somehow. Or at least, understand why it does not work.

Thanks,
Rares
 
K

Kai-Uwe Bux

Rares said:
Hello,

I am trying to write a generic function that can read data from a file
and store it in a container.

I defined the following function:

template<class T>
void readBinaryFile(
const std::string &filename,
std::iterator<std::eek:utput_iterator_tag, T> data)
{
...
}

You probably don't want that signature:

(a) When you pass an iterator, it will be sliced down to std::iterator,
which is a rather meaningless thing.

(b) Even if you fix that (e.g., passing the iterator by reference), you
would limit the use of your function to iterator types derived from
std::iterator< output_iterator_tag, T >. There are not that many.

You should consider:

template < typename T, typename InIter >
void readBinaryFile(
std::string const & filename,
InIter where ) {
....
}

which is much more versatile.

Because the function is reading binary data, it needs to know the type of
the elements in the container, T.

When I call it with something like:

vector<float> f;
readBinaryFile<float>("test.bin", back_inserter(f));

I get:

error: no matching function for call to ?readBinaryFile(std::string&,
std::back_insert_iterator<std::vector<float, std::allocator<float> > >)?

I am a little bit confused because I know that the back_insert_iterator
is an output iterator. In fact, back_insert_iterator is defined as:

template<typename _Container>
class back_insert_iterator
: public iterator<output_iterator_tag, void, void, void, void>

Note that float != void. You would want that back_inserter to be derived
If I change the function declaration to:

template<class T, class OutputIterator>
void readBinaryFile(
const std::string &filename,
OutputIterator data)

it works fine.

Aha.
If possible, I would prefer to make the first declaration work
somehow.

No, you would not :)
Or at least, understand why it does not work.

See above.


Best

Kai-Uwe Bux
 
J

Jerry Coffin

[email protected] says... said:
I defined the following function:

template<class T>
void readBinaryFile(
const std::string &filename,
std::iterator<std::eek:utput_iterator_tag, T> data)
{
...
}

That was your first mistake... :)
Because the function is reading binary data, it needs to know the type of
the elements in the container, T.

Yup.

The function want is already implemented. It's called std::copy.

All you need to do is write an extractor that extracts an item from your
file correctly. The one problem is that there are already overloads to
read the data incorrectly (i.e. from a text file) for many built-in
types. You can overcome this by creating a proxy class, something like:

template <class T>
class binIO {
T data;
public:
operator T() const { return data; }

binIO(T value = T()) : data(value) { }

friend std::istream &operator>>(std::istream &is, binIO &f) {
return is.read((char *)&f.data, sizeof(f.data));
}
friend std::eek:stream &operator<<(std::eek:stream &os, binIO const &f) {
return os.write((char *)&f.data, sizeof(f.data));
}
};

To read (for example) a file of floats in binary format into a vector,
you'd do something like:

std::vector<float> data;
std::ifstream in("input.dat", std::ios::binary);

std::copy(std::istream_iterator<binIO<float> >(in),
std::istream_iterator<binIO<float> >(),
std::back_inserter(data));
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,900
Latest member
Nell636132

Latest Threads

Top