views:

261

answers:

4

This may not be possible, but I figured I'd ask...

Is there any way anyone can think of to track whether or not an automatic variable has been deleted without modifying the class of the variable itself? For example, consider this code:

const char* pStringBuffer;
{
    std::string sString( "foo" );
    pStringBuffer = sString.c_str();
}

Obviously, after the block, pStringBuffer is a dangling pointer which may or may not be valid. What I would like is a way to have a wrapper class which contains pStringBuffer (with a casting operator for const char*), but asserts that the variable it's referencing is still valid. By changing the type of the referenced variable I can certainly do it (boost shared_ptr/weak_ptr, for example), but I would like to be able to do it without imposing restrictions on the referenced type.

Some thoughts:

  • I'll probably need to change the assignment syntax to include the referenced variable (which is fine)
  • I might be able to look at the stack pointer to detect if my wrapper class was allocated "later" than the referenced class, but this seems hacky and not standard (C++ doesn't define stack behavior). It could work, though.

Thoughts / brilliant solutions?

A: 

One technique you may find useful is to replace the new/delete operators with your own implementations which mark the memory pages used (allocated by your operator new) as non-accessible when released (deallocated by your operator delete). You will need to ensure that the memory pages are never re-used however so there will be limitations regarding run-time length due to memory exhaustion.

If your application accesses memory pages once they've been deallocated, as in your example above, the OS will trap the attempted access and raise an error. It's not exactly tracking per se as the application will be halted immediately but it does provide feedback :-)

This technique is applicable in narrow scenarios and won't catch all types of memory abuses but it can be useful. Hope that helps.

Henk
+1  A: 

In general, it's simply not possible from within C++ as pointers are too 'raw'. Also, looking to see if you were allocated later than the referenced class wouldn't work, because if you change the string, then the c_str pointer may well change.

In this particular case, you could check to see if the string is still returning the same value for c_str. If it is, you are probably still valid and if it isn't then you have an invalid pointer.

As a debugging tool, I would advise using an advanced memory tracking system, like valgrind (available only for linux I'm afraid. Similar programs exist for windows but I believe they all cost money. This program is the only reason I have linux installed on my mac). At the cost of much slower execution of your program, valgrind detects if you ever read from an invalid pointer. While it isn't perfect, I've found it detects many bugs, in particular ones of this type.

Chris Jefferson
Yeah; Purify would do similar things, back when it actually worked and didn't just corrupt your application at runtime. Unfortunately it's a runtime app, costs money, and doesn't work in any application approaching commercial complexity. =/
Nick
A: 

You could make a wrapper class that works in the simple case you mentioned. Maybe something like this:

X<const char*> pStringBuffer;
{
    std::string sString( "foo" );
    Trick trick(pStringBuffer).set(sString.c_str());
} // trick goes out of scope; its destructor marks pStringBuffer as invalid

But it doesn't help more complex cases:

X<const char*> pStringBuffer;
{
    std::string sString( "foo" );
    {
        Trick trick(pStringBuffer).set(sString.c_str());
    } // trick goes out of scope; its destructor marks pStringBuffer as invalid
}

Here, the invalidation happens too soon.

Mostly you should just write code which is as safe as possible (see: smart pointers), but no safer (see: performance, low-level interfaces), and use tools (valgrind, Purify) to make sure nothing slips through the cracks.

John Zwinck
A: 

Given "pStringBuffer" is the only part of your example existing after sString goes out of scope, you need some change to it, or a substitute, that will reflect this. One simple mechanism is a kind of scope guard, with scope matching sString, that affects pStringBuffer when it is destroyed. For example, it could set pStringBuffer to NULL.

To do this without changing the class of "the variable" can only be done in so many ways:

  • Introduce a distinct variable in the same scope as sString (to reduce verbosity, you might consider a macro to generate the two things together). Not nice.

  • Wrap with a template ala X sString: it's arguable whether this is "modifying the type of the variable"... the alternative perspective is that sString becomes a wrapper around the same variable. It also suffers in that the best you can do is have templated constructor pass arguments to wrapped constructors up to some finite N arguments.

Neither of these help much as they rely on the developer remembering to use them.

A much better approach is to make "const char* pStringBuffer" simply "std::string some_meaningful_name", and assign to it as necessary. Given reference counting, it's not too expensive 99.99% of the time.