Thread: Is my class suitable for iterator-like behavior?

  1. #1
    Guest
    Guest

    Is my class suitable for iterator-like behavior?

    I have a class which opens text files and returns roughly equally-sized chunks of std::string each time a public method next() is called, like so:
    Code:
    int chunks = 32;
    Read summary("path/to/file.txt", chunks);
    for(int i = 0; i < chunks; ++i)
    {
        summary.next(); // hand over to thread...
    }
    I thought it would be neater if I could do:
    Code:
    Read summary("path/to/file.txt", chunks);
    for(auto chunk : summary)
    {
        *chunk;
    }
    Is is possible to implement something like that? And would I need to inherit from e.g. std::iterator<input_iterator_tag, std::string> (adapting all methods) for the foreach loop to work, or can I just implement from scratch the methods that I want to support (increment, deference, compare) and be done with it?

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Isn't a chunk of a std::string a std::string in itself rather than an iterator?
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    Guest
    Guest
    Yes, maybe I worded that badly.

    So, simplified next() looks like this:
    Code:
    std::string next()
    {
        std::string s;
        // fill 's' with a piece of the text file
        return s;
    }
    And I'm wondering if something like:
    Code:
    std::string operator*()
    {
        std::string s;
        // ...
        return s;
    }
    is possible.

    Of course I'd need to have cbegin, cend etc. and maybe that's not trivial at all.

  4. #4
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Yes, it is possible, except that next is a member function of Read whereas I would expect operator* to be overloaded for Read's iterator class. Inheriting from std::iterator is intended as a convenience so that some of the typical boilerplate can be automatically provided.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  5. #5
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by Guest View Post
    Is is possible to implement something like that? And would I need to inherit from e.g. std::iterator<input_iterator_tag, std::string> (adapting all methods) for the foreach loop to work, or can I just implement from scratch the methods that I want to support (increment, deference, compare) and be done with it?
    It's certainly possible. You need to implement at the least begin/end or cbegin/cend if you don't intend for the user to be able to modify whatever the iterators point to. Then in those begin and end functions, you need to return iterators. Since I assume that the entire file isn't read into memory at construction, but instead uses lazy reading, you will need some kind of iterator class. You can then choose from different iterator categories, but you should implement all the functionality in such categories. You can choose from forward only, bidirectional and random iterators. For your iterator class, you can inherit from std::iterator and it will provide the necessary boilerplate for you. You can also do it yourself. See std::iterator - cppreference.com.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  6. #6
    Guest
    Guest
    Quote Originally Posted by Elysia View Post
    (...) Since I assume that the entire file isn't read into memory at construction, but instead uses lazy reading, you will need some kind of iterator class. (...)
    That's the way it is. Thanks for the guidance you two, I'll see how far I can get and post again if I get stuck.
    Last edited by Guest; 04-28-2015 at 09:57 AM. Reason: typo

  7. #7
    Guest
    Guest
    When I made this thread, I had some fundamental misconceptions about iterators that I now cleared up, but I'm still struggling on implementation details. I feel like I'm 80% towards "getting it". Unfortunately I found few articles on the topic, and many were surprisingly old for something living on the web.

    So let's say class Read, as described in my original post, lazily reads part of a file into a std::string and returns it. Another class ReadIter presents an iterator to Read.

    I assume the deference operator would look something like this:
    Code:
    std::string& ReadIter::operator*()
    How would I go about accessing Read's method to get the string? Do I pass a reference to my Read object to ReadIter's constructor and store it in there? I'm not sure what the common approach is.

    Also, since Read has no "last element" as such in memory, how does the iterator know when to stop? I thought that since Read knows the number of chunks to slice the file into, maybe I could use that value to denote the last element and have the iterator keep track of its current "index" through a member? At the same time, this doesn't mesh with the iterator being constructed with a Read object. As you can see, I'm not sure how to approach this.

  8. #8
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by Guest View Post
    I assume the deference operator would look something like this:
    Code:
    std::string& ReadIter::operator*()
    Most likely you should return a const reference since even if modifying the string, it will not reflect in the file. Besides, it's read iterator, and reads semantically should be const.

    How would I go about accessing Read's method to get the string? Do I pass a reference to my Read object to ReadIter's constructor and store it in there? I'm not sure what the common approach is.
    Yep, that's the common approach. An iterator is associated with a certain class instance. So it's typically undefined to try to compare iterators from two different objects, so it should have an instance to the object it is associated with.

    Also, since Read has no "last element" as such in memory, how does the iterator know when to stop? I thought that since Read knows the number of chunks to slice the file into, maybe I could use that value to denote the last element and have the iterator keep track of its current "index" through a member? At the same time, this doesn't mesh with the iterator being constructed with a Read object. As you can see, I'm not sure how to approach this.
    Iterators are positions in a file, so you can have multiple iterators from the same object and they can all be iterated without affecting each other. That means the iterator should store the necessary state, and not the parent object.
    That said, for how the iterator knows to stop, this is where we're going to just have to be clever. If the read object knows how many elements there are, then an iterator could keep track of the current position and the total position.
    If you don't keep track of how many objects there are, you could simply use a flag to indicate end, or some index value like -1. Then you have to use special comparison logic to ensure an iterator that has been advanced beyond the end of file compares as expected towards such a "special" iterator. It's up to you to be clever here.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. How is iterator implemented in vector class?
    By freiza in forum C++ Programming
    Replies: 4
    Last Post: 06-10-2012, 08:14 AM
  2. iterator int template class
    By C_ntua in forum C++ Programming
    Replies: 11
    Last Post: 10-19-2008, 01:47 AM
  3. Normal iterator pointer behavior
    By SevenThunders in forum C++ Programming
    Replies: 9
    Last Post: 04-01-2008, 12:11 PM
  4. template class does not like list iterator
    By lowerlogic in forum C++ Programming
    Replies: 4
    Last Post: 06-21-2007, 11:04 PM
  5. Help with defining an iterator class
    By Dragoon_42 in forum C++ Programming
    Replies: 2
    Last Post: 02-04-2003, 06:26 PM