Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How can I couple the lifetimes of two variables?

The following code compiles and runs fine, but does not work as intended (compare the comments in the code). I believe the reason is that the lifetime of is ends with getFileIter, so the stream buffer iterator has nothing left to iterate over. (By contrast, I think I remember that in C#, is would live as long as the stream buffer iterator points to, but that does not seem to be the case in C++.)

One fix to avoid this is using ... *is = new std::ifstream(p), but this means I have to take care of the pointer myself when I’m done.

Is there any mechanism to link the lifetimes of is and its stream buffer iterator?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

// g++ test.cpp -o test && ./test

#include <iostream>
#include <fstream>

std::istreambuf_iterator<char> getFileIter(std::string p)
{
  std::ifstream is(p);
  //std::ifstream *is = new std::ifstream(p);
  std::cout << "Read " << p << "? " << is.good() << std::endl;
  return std::istreambuf_iterator<char>(is.rdbuf());
}

int main()
{
  std::string p1("/etc/passwd");
  std::string p2("/etc/group");

  // Prints 1 as if the two files were identical
  std::cout << std::equal(
    getFileIter(p1),
    std::istreambuf_iterator<char>(),
    getFileIter(p2)
  ) << std::endl;

  // Prints nothing except /////
  auto x = getFileIter(p1);
  int i = 0;
  do
  {
    std::cout << *(x++) << "/";
    i++;
  } while (i < 5);
  std::cout << std::endl;

  return 0;
}

>Solution :

As one comment suggests, combine both things: stream and its iterator in a struct – to ensure the iterator will not outlife the stream:

struct FileIter
{
    FileIter(const std::string& p) 
        : is(p), iter(is.rdbuf())
    {}
    std::ifstream is;
    std::istreambuf_iterator<char> iter;
};

  std::cout << std::equal(
    FileIter(p1).iter,
    std::istreambuf_iterator<char>(),
    FileIter(p2).iter
  ) << std::endl;

  FileIter x(p1);
  int i = 0;
  do
  {
    std::cout << *(x.iter++) << "/";
    i++;
  } while (i < 5);
  std::cout << std::endl;

If you like — you might make this FileIter looks like iterator – see this post

Two important things:

In this code:

  std::cout << std::equal(
    FileIter(p1).iter,
    std::istreambuf_iterator<char>(),
    FileIter(p2).iter
  ) << std::endl;

FileIter might be temporary (nameless) because its lifetime is guaranteed to be as long as to the end of expression (the semicolon).

Here, you cannot replace FileIter x(p1); with auto x = FileIter(p1).iter; because stream in FileIter will end its lifetime with the end of expression (semicolon here).

  FileIter x(p1);
  int i = 0;
  do
  {
    std::cout << *(x.iter++) << "/";
    i++;
  } while (i < 5);
  std::cout << std::endl;

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading