How to detect if a ptr is still referencing a valid reference after that reference goes out of scope
Asked Answered
S

6

14

I am toying around with streams for a bit and can't get my head around the following.

Here we have a basic ostream ptr that is set to different output streams, whether it is cout, cerr or a file.

// ostream ptr
std::ostream* outstream;

// set output ostream
void setOutput(std::ostream & os)
{
  outstream = &os; 
}

// write message to ostream
void writeData(const std::string & msg)
{    
  *outstream << msg << '\n';
}

int main (int argc, char * const argv[]) 
{
  // init to std out
  setOutput(std::cout);
  writeData("message to cout");

  setOutput(std::cerr);
  writeData("message to cerr");

  std::ofstream fileout("test.txt", std::ofstream::out | std::ofstream::app);
  setOutput(fileout);
  writeData("message to file");
  //fileout.close();

  setOutput(std::cout);
  writeData("message2 to cout");

  return 0;
}

The above works perfectly and shows the strength of the c++ iostream implementation. Perfect.

However, since the setOutput is set by reference the referenced object has to stay in scope. This is where the issue emerges. I want to figure out a way to default the output to std::cout if the ofstream or any other ostream is invalidated. That is, referenced object is or went out of scope.

For example:

// write message to ostream
void writeData(const std::string & msg)
{
  if (/*stream or memory is invalid*/)
    setOutput(std::cout);

  *outstream << msg << '\n';
}
// local fileout goes out of scope
void foo()
{
  std::ofstream fileout("test.txt", std::ofstream::out | std::ofstream::app);
  setOutput(fileout);
  writeData("message to file");
}

int main (int argc, char * const argv[]) 
{
  setOutput(std::cout);
  writeData("message to cout");

  foo();
  /* problem the local fileout is no longer referenced by the ostream ptr*/
  /* the following should be redirected to std::cout cuz of default*/
  writeData("message2 to cout");

  return 0;
}

The above is fine until the foo() returns to the main function. There it goes horrible wrong because the locally defined ofstream is not reachable anymore.

Obviously this is not advisable and the user should realise this. However I want to wrap all this in a logging class and thus keep the state of the object valid even thought this misuse might happen. It will cause an invalidate access violation which can be hard to find.

Concrete question. Is there any way to figure out whether an ostream ptr or any ptr for that matter is still referencing a valid object or memory location?

ps: I could use heap memory and do something with smart pointers but frankly I'd want to keep it like this if possible

Spiral answered 4/5, 2016 at 19:54 Comment(7)
"Concrete question. Is there any way to figure out whether an ostream ptr or any ptr for that matter is still referencing a valid object or memory location?" -- yes : Java.Catalano
@Catalano don't like javaSpiral
@Captain Obvlious but its all on the stack. I tried that but you still can't detect whether it went out of scope afaikSpiral
@EthanF. What exception? Accessing an invalid object results in undefined behavior not a C++ exception.Hadst
@CaptainObvlious Yeah, you are right. it can be anything in that address.Lionize
Make your fileout a global object too. (You can still call fileout.open on it from the stack). Then it'll have a static lifetime and your global std::ostream* can point to it.Ransom
@PSkocik I understand, but that is completely missing the pointSpiral
R
13

Concrete question. Is there any way to figure out whether an ostream ptr or any ptr for that matter is still referencing a valid object or memory location?

No. There is no way to figure that out with raw pointers. Not in standard c++ at least.

You will need to guarantee that the pointed object stays alive as long as it's pointed to.

A common pattern that is used to provide that guarantee is RAII, as detailed in other answers. Another approach to guaranteeing validity of of a pointer is to use a smart pointer instead of a raw one. However, those are not compatible with automatic variables.

It would be OK to keep pointing to dead objects as long as you could guarantee that the pointer is not dereferenced. Which is often difficult to guarantee, because, as already stated, there is no way to test whether the pointed object exists.

Radiopaque answered 4/5, 2016 at 20:9 Comment(6)
@Catalano 'raw' pointers are proper pointers. I used 'raw' qualifier to distinguish from smart pointers which can also be considered to be pointers from an abstract point of view, even though they aren't proper pointers. I needed to distinguish, because in the case of some smart pointers, you can figure out whether the pointed object is still valid and in the case of others, the smart pointer itself takes care of destroying the object when it is no longer referenced so there is no need to figure it out.Radiopaque
@Catalano To expand on user2078303's comment, the desire to be able to do things like the OP wants to do is so great, that we've invented all sorts of "pointers" (classes which point) which provide guarantees about the lifespan of an object. These have gotten popular enough that people have started calling pointers "raw pointers" to take extra care to point out how few guarantees they come with, and gently encourage people to use more smart pointers. I've also heard of them called "native pointers," but "raw" is more common.Beachhead
They're called "raw pointers" because they're what paleo C programmers eat for breakfast.Birdhouse
@HotLicks I eat Pop Tarts and Mt Dew for breakfast because I am a Real C Programmer.Catalano
Including a mention to weak_ptr and shared_ptr will greatly improve the answer.Debbydebee
@Debbydebee I didn't think that smart pointers would be very relevant to this question since I think using them for this particular case would be a bit kludgy. Anyway, I added a footnote mentioning smart pointers for the general case of guaranteeing validity of objects. I didn't go into detail, but a reader will be able to search more detailed information from other questions.Radiopaque
I
10

This sounds like a great use case for RAII.

Write a class that takes a filename and a std::ostream** as parameters to its constructor. In the constructor of the said class, construct the ofstream (as a member), and set the pointer to the ofstream. In the destructor, revert to stdout.

Then, replace the first two lines of the following function with a declaration of the new class.

void foo()
{
  std::ofstream fileout("test.txt", std::ofstream::out | std::ofstream::app);
  setOutput(fileout);
  writeData("message to file");
}
Information answered 4/5, 2016 at 20:7 Comment(0)
R
5

You should use RAII to force the stream to be set correctly and then set back to std::cout if the object is destroyed.

class OutputStream
{
    protected:
        static std::ostream*& internalGlobalStateOfOutputStream()
        {
            static std::ostream*  out = &std::cout;
            return out;
        }
    public:
        static std::ostream& getOutputStream()
        {
            return *internalGlobalStateOfOutputStream();
        }
};
template<typename T>
class OutputStreamOwner: public OutputStream
{
    T  ownedStream;
    public:
        OutputStreamOwner(T&& obj)
            : ownedStream(std::move(obj))
        {
            internalGlobalStateOfOutputStream() = &ownedStream;
        }
        template<typename... Args>
        OutputStreamOwner(Args... args)
            : ownedStream(args...)
        {
            internalGlobalStateOfOutputStream() = &ownedStream;
        }
        ~OutputStreamOwner()
        {
            internalGlobalStateOfOutputStream() = & std::cout;
        }
        // Delete copy
        OutputStreamOwner(OutputStreamOwner const&)           = delete;
        OutputStreamOwner& operator(OutputStreamOwner const&) = delete;
};

The usage is:

void foo()
{
  OutputStreamOwner<std::ofstream>  output("test.txt", std::ofstream::out | std::ofstream::app);

  writeData("message to file");
}
Razzia answered 4/5, 2016 at 20:16 Comment(0)
J
3

A possible approach is to create a RAII class that wraps the stream before passing it into setOutput. This class should be designed to work like shared_ptr such that it maintains a shared ref count. writeData then checks to see if it has the only remaining reference and if so then destroys the ostream and defaults to cout.

Janiecejanifer answered 4/5, 2016 at 20:5 Comment(0)
P
2

You could avoid these complications altogether with a function that takes the stream as input.

void writeData(std::ostream& os, const std::string & msg)
{    
     os << msg << '\n';
}

You can further refine it by returning the stream, to allow one to chain calls to it:

std::ostream& os writeLine(std::ostream& os, const std::string & msg)
{    
     os << msg << '\n';
     return os;
}

// declare stream
stream << writeLine(stream, "Foo") << writeLine(stream, "Bar");

In fact this function is nicer and easier to maintain, as you don't have to remember which stream is set at any given time. For large programs, this is an important quality.

Panga answered 5/5, 2016 at 6:45 Comment(0)
J
1

Concrete question. Is there any way to figure out whether an ostream ptr or any ptr for that matter is still referencing a valid object or memory location?

ps: I could use heap memory and do something with smart pointers but frankly I'd want to keep it like this if possible

No, there is no standard way to test if a raw pointer or reference is still referring to a valid object.

RAII is the standard C++ solution to this type of problem so you should be looking at smart pointers, in my opinion. I am not aware of any library provided smart pointer that would solve this particular problem but an RAII solution based on shared ownership seems the best solution here.

Janiecejanifer answered 4/5, 2016 at 20:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.