th3g3ntl3man
th3g3ntl3man

Reputation: 2106

C++ - Overloading of operators needed for an iterator

I'm trying to create an iterator on a library that allows reading a specific file format.

From the docs, to read the file content you need do something like this:

CKMCFile database;

if (!database.OpenForListing(path)) {
  std::cerr << "ERROR: unable to open " << path << std::endl;
}

CKMCFileInfo info;
database.Info(info);

CKmerAPI kmer(info.kmer_length);

uint32 cnt;
std::vector<uint64_t> data;
std::vector<uint64> ulong_kmer;
data.reserve(info.total_kmers);

while (database.ReadNextKmer(kmer, cnt)) {
  kmer.to_long(ulong_kmer);
  data.push_back(ulong_kmer[0]);
}

Now, I started with this class wrapper:

class FileWrapper {
  CKMCFile database;
  CKMCFileInfo info;
  Iterator _end;

  public:
    explicit FileWrapper(const std::string &path) {
      if (!database.OpenForListing(path)) {
        std::cout << "ERROR: unable to open " << path << std::endl;
      }

      database.Info(info);
    }

    Iterator begin() {
      Iterator it;
      it.database = &database;
      it.total = 0;

      uint32_t cnt;
      std::vector<uint64_t> ulong_kmer;

      CKmerAPI tmp(info.kmer_length);
      database.ReadNextKmer(tmp, cnt);
      tmp.to_long(ulong_kmer);

      return it;
    }
    
    Iterator end() const { return _end; }
    uint64_t size() { return info.total_kmers; }
};

And then, this is the Iterator class:

class Iterator {
    friend class FileWrapper;

    CKMCFileInfo info;
    CKMCFile *database;
    uint64_t kmer, total;
    
    public:
      Iterator &operator++() {
        ++total;

        uint32_t cnt;
        std::vector<uint64_t> ulong_kmer;

        CKmerAPI tmp(info.kmer_length);
        database->ReadNextKmer(tmp, cnt);
        tmp.to_long(ulong_kmer);

        return *this;
      }

      bool operator<(const Iterator &rhs) const { return total < rhs.total; }
      uint64_t operator*() const { return kmer; }
  };

But, during some test I can't use into a for loop for something like for (auto it = begin(); it != end(); ++i) { ... } or begin() + size(). How can I overload correctly this two operatos? opeartor!= and operato+

Upvotes: 0

Views: 831

Answers (1)

igel
igel

Reputation: 556

You'll have to think about 2 major things before:

  1. Ownership. Currently, you have to make sure your FileWrapper survives at least as long as any Iterator returned from it by calling its begin() (since your Iterators store pointers to data owned by the FileWrapper object). If you cannot guarantee that, maybe think about using unique_ptrs or shared_ptrs
  2. Iterator Category. As discussed in the comments, it appears that your database requires you to use "input iterators". They can only be incremented by one (do not provide operator+(int)) and dereferenced. Indeed, what would the iterator begin() + 10 look like? If this should advance your file-pointer, then you cannot define the end as begin() + size() as that would just skip through the file.
  3. Representation. What should an end-iterator look like? A simple choice might be to indicate the end with database == nullptr. In this case, an operator!= might look like this:
bool is_end() const { return database == nullptr; }

bool operator==(const Iterator& other) const {
  if(is_end()) return other.is_end();
  if(other.is_end()) return false;
  return (database == other.database) && (total == other.total);
}

bool operator!=(const Iterator& other) const { return !operator==(other); }

Now, you'll need code that ensures that all end-iterators have database == nullptr and, whenever a non-end iterator becomes and end-iterator by application of operator++(), you'll need to set database = nullptr and total = 0 (or something).

A note at the end: your Iterators may be in an inconsistent state after construction and before assignment of their database member. It is prudent to declare a proper constructor for Iterator that initializes its members.

EDIT: here's a suggestion for an integration

Upvotes: 1

Related Questions