Monday, May 07, 2007

Custom deleters with smart pointers

There is just one smart pointer as part of the standard C++ library as of now (excluding the tr1 and proposals). That is the std::auto_ptr.

It is crucial to know that you can only use it for allocations made via the new operator. It neither handles, the allocations made via the array form of new or malloc or any other allocation routine provided, for that matter.

How can you extend it to be able to use it with those? Basically, you can't. There are an option though - you lay down your own version of auto_ptrs specific to those which have the delete call replaced by free/delete[]/or the relevant deleter. This will ask for writing down multiple classes for them and using them as appropriate. There is another way to extend it but before that let's look at how boost::shared_ptr handles it.

Boost shared_ptr has a concept for a custom deleter. The deleter would operate on the pointer (when the reference count drops to 0 - remembers the ownership is shared!) and the effect should be that of freeing the resource. The following is an illustration:

[CODE]

template<typename T>
struct mallocDeleter
{
    void operator() (T*& ptr)
    {
        if (ptr)
        {
            free(ptr);
            ptr=NULL;
        }
    }
};
void somefunction()
{
    MyType* ptr = getAllocatedPointer();//returns pointer to memory allocated by malloc
    shared_ptr<MyType, mallocDeleter<MyType> >object(ptr, mallocDeleter());
}

//scope ends shared_ptr's destructor gets called that then called mallocDeleter(ptr) - The default deleter is "delete".

Notice, how this expresses the flexibility. You could have acquired any other resource and would have wrapped it inside a shared_ptr and would provide the deleter to free that resource. For example, a File Handle - on which the custom deleter calls close (for C++ fstream objects, you don't need that - they already exploit RAII). Or, a db connection handle, for which the deleter would handle closing it. Just about anything that is manually allocated! Amazing how the ability to provide the deleter makes this smartness so generic.

I wonder why the auto_ptr or the boost scoped_ptr don't provide this ability. RAII is truely magic and takes care of resource leak issues so well.

Cheers!

5 comments:

Anonymous said...

You have an interesting web log I linked to your weblog and will be happy if you do so.
http://enhamid.blogspot.com/

kroiz said...

nice post.
and btw in the mallocDeleter the line:
ptr=NULL;
is useless since when the control returns to the caller the pointer there will still point to where it pointed before.

abnegator said...

Hi Kroiz,

Well spotted! Thank you! Will try to update the post soon.

Regards,
Abhishek

Anonymous said...

Hi,
I'm dealing with some OCCI code that consist of DAO class that contains methods that go like this:
void method() {
create statement
...
...
get resultset
close resultset
terminate statement
}
In order to use RAII to control statement management I must pass in the constructor pointers to all the objects involved in destruction of the statement - connection, statement, resultset. E.g. in order to close the statement you got to call conn->terminateStatement(stmt);.
This results in necessity to provide the policy for passing the handles to RAII object.

Aman Aggarwal said...

Have a look at https://github.com/codenrun/auto_ptr_custom