views:

178

answers:

1

Hi there,

in the past we encountered various memory-leaks in our software. We found out that these happened mostly due to incorrect usage of our own "free"-Methods, which free Queue-Message-Data and the likes.

The problem is, in our deepest tool-functions there are two methods to free up dynamically allocated memory, with the following signatures:

void free (void *pData);
void free (void **ppData);

Both methods basically do the same thing, except the second one dereferences the data first. I know that it is possible to do everything with just one of these two, but lets just say that the software was designed that way many years ago and now theres code everywhere using both.

The problem comes in, when somebody implements a call to these methods like this:

QueueMessage *pMsg;
pMsg = myQueue.read(...); // dynamically allocates space for the msg and fills it
// Do something
myQueue.free(&pMsg); // << WRONG!

The code above should pass the pointer to the message to the free-method. Basically it would work, but since the compiler doesn't know which function to use in this case, it uses the free(void *pData) method which then tries to free the Pointer, not the memory which the Pointer points to. Of course, the solution is easy, either:

myQueue.free(pMsg);
or
myQueue.free((void**)&pMsg);

Both will work. Now that I've described the problem and the solution, I'd like to know: Is there any way I can ensure that the programmer using these methods uses them the right way? I've read about header annotations in VS2005, which are quite useful, but don't seem to do what I need. It would be great if there is a way to produce a warning if the reference of a pointer is passed to the first method, so the programmer at least gets a hint that there's something wrong with his code.

By the way, I'm using Microsoft VS2005 and have the possibility to upgrade to VS2008 if needed. It's a C++ Application migrated to VS2005, and therefore .NET-compatible.

+5  A: 

Try templates:

template <class T> void free (T *pData);
template <class T> void free (T **ppData);

This should give you an exact match on the argument type and, hence, the compiler should call the correct implementation.

You can either replace your original void-pointer implementations of free with the templated versions, or have the templated versions call the original void-pointer versions:

template <class T> void free (T *pData)
{
    free(static_cast<void *>(pData));
}

template <class T> void free (T **ppData)
{
    free(static_cast<void **>(ppData));
}

Edit: So what was going on in the original version?

The rules for overloaded function resolution are fairly complicated, but in principle, the compiler first tries to find an exact math for the argument types and, if it can't find them, there is a variety of automatic type conversions it can apply.

One of these automatic type conversions is that any pointer -- including a pointer-to-pointer -- can be converted to a void * pointer. There is, however, no rule that a typed pointer-to-pointer (T **) can be converted automatically to a void pointer-to-pointer (void **). This is why all of your calls were going to free(void *).

When you introduce the templated versions, this changes. The compiler can now find an exact match for every call that you make -- either free(T *) or free(T **) -- and this enables it to call the correct version.

Martin B
Excellent +1: These can be added to the header with the other declarations and have them do the casts for you - maybe you could add some simple definitions showing the calls to the other functions.
Richard Corden
Good suggestion -- I've edited the answer accordingly.
Martin B
You, sir, just made my day!This works perfect, and it does even more than I ever expected, instead of generating warnings, it will simply solve all memory leaks which happen because of this programming error. Thanks so much!One question though, I don't understand the statement completely. Would this template-construction make actually any sense or is it just a null-instruction telling the compiler to take a closer look on the parameter-types?
Excelcius
Sorry about the delay -- I've added an explanation to the answer to clarify what was going on originally and what the templated version changes about this.
Martin B