I frequently find myself having to use C based APIs to allocate and
deallocated resources. Using these APIs requires remembering to
deallocate all the resources through special deallocation functions
(not to mention exception safety). I would really like a general
"smart pointer/resource" or raii_ptr<> class that I can access as a
regular pointer and that accepts a custom deallocator.
Though boost::shared_ptr might suffice, it seems to convay the wrong
message since these resources are generally noncopyable nor sharable.
All I really need is scope_ptr with a custom deleter.
I was wondering why boost::scoped_ptr does not support custom
deleters. Is there a rational behind this?
In this article (http://www.artima.com/cppsource/bigtwo3.html#refs),
Bjorn Karlsson and Matthew Wilson propose such a hypothetical RAII
class for demonstration purposes. Does anyone know of a more
production-level implementation of such an idea?
Thanks,
Adi
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
> I frequently find myself having to use C based APIs to allocate and
> deallocated resources. Using these APIs requires remembering to
> deallocate all the resources through special deallocation functions
> (not to mention exception safety). I would really like a general
> "smart pointer/resource" or raii_ptr<> class that I can access as a
> regular pointer and that accepts a custom deallocator.
>
> Though boost::shared_ptr might suffice, it seems to convay the wrong
> message since these resources are generally noncopyable nor sharable.
> All I really need is scope_ptr with a custom deleter.
>
You ask for ScopeGuard:
http://www.moderncppdesign.com/publications/cuj-12-2000.html
or advanced (and slower) variant
http://tnfox.sourceforge.net/TnFOX/html/group__rollbacks.html
/Pavel
>
> I was wondering why boost::scoped_ptr does not support custom
> deleters. Is there a rational behind this?
boost::shared_ptr does support custom deleters.
example
class A {/* content not needed for example */};
A *createA();
void destoryA();
struct A_deleter{void operator () (A *a){destroyA();};
boost::shared_ptr<A> p(createA(),A_deleter());
This creates a ref counted pointer to A that will call destroyA when
it is time to actually time to destroy A.
Why not create your own raii_resource class, and use a smart_ptr to it?
It seems the most flexible and loosly coupled solution.
Best,
John
--
John Torjo, Contributing editor, C/C++ Users Journal
-- "Win32 GUI Generics" -- generics & GUI do mix, after all
-- http://www.torjo.com/win32gui/
-- v1.5 - tooltips at your fingertips (work for menus too!)
+ bitmap buttons, tab dialogs, hyper links, lite html
Adi
> Well, the ScopeGuard and its ilk, while being VERY useful, is not
> exactly what I want.
> For one, I'd like pointer semantics, as in most smart pointers.
> Secondly, although it takes care of exception safety, remembering to
> dismiss the guard, brings me back to remembering to deallocate my
> resource at the end of the scope.
Why? You only need ScopeGuard Dismiss() if you pass the ownership of the
resource to some other object. In this case you could probably use the
other object from the beginning instead Scopeguard anyway. And you will
notice very quickly when you have forgotten to Dismiss :-)
In my projects I mostly use ON_BLOCK_EXIT macro (which is essentially a
ScopeGuard without Dismiss()). It's very handy for encapsulating the C
level calls. For example:
FILE* f = popen("cat /etc/passwd", "r");
if (f) {
ON_BLOCK_EXIT(pclose, f);
// ...
throw "some exception";
// ...
}
The only problem with ON_BLOCK_EXIT is that one has to remember to switch
off the "Debug and continue" feature in MS Visual C++. This is not fixed
even in .NET 2003.
Regards
Paavo
IIRC, your second observation is also the reason. I believe
shared_ptr stores the custom deleter along with the reference count on
the heap. Adding custom deleter to scoped_ptr would double the size
of the scoped_ptr itself and require an additional heap allocation.
Conversely, the built-in overhead of shared_ptr "hides" the cost of
the custom deleter.
I get the impression that scoped_ptr was designed exclusively to be a
good solution to a very specific problem, and that it hasn't received
the sort of intense scrutiny and refinement that shared_ptr has. A
general-purpose scope guard might serve your purposes better. For an
example, see:
http://www.cuj.com/documents/s=8000/cujcexp1812alexandr/alexandr.htm
>
> I was wondering why boost::scoped_ptr does not support custom
> deleters. Is there a rational behind this?
My move_ptr library provides a lightweight smart pointer type which can do this,
if you don't mind the strange name 'static_move_ptr' (I'd change the name in a
second if I could come up with a better one):
http://home.comcast.net/~jturkanis/move_ptr/
Here's a sample use
struct thing {
void destroy() { delete this; }
};
struct destroyer {
template<typename T>
void operator() (T* t) const { t->destroy(); }
};
int main()
{
using namespace boost;
static_move_ptr<thing, destroyer> ptr1(new thing);
static_move_ptr<thing, destroyer> ptr2(new thing, destroyer());
// both instances of thing will be destroyed at end of block
}
> In this article (http://www.artima.com/cppsource/bigtwo3.html#refs),
> Bjorn Karlsson and Matthew Wilson propose such a hypothetical RAII
> class for demonstration purposes. Does anyone know of a more
> production-level implementation of such an idea?
I haven't looked at their implementation, so I can't say if mine is more
'production-level', but it passes some rigorous regression tests and is used
internally by Thorsten Ottosen's Smart Container library currently under review
at Boost.
>
> Thanks,
> Adi
>
Jonathan
The main reason is that boost::scoped_ptr has users that don't need
custom deleters, but do need the guarantee that sizeof(scoped_ptr<T>)
== sizeof(T*). It is possible to add custom deleter support to
scoped_ptr and preserve the size guarantee in the common case when the
deleter is an empty class, but we haven't done it. :-) Mostly because
the focus was on making shared_ptr and weak_ptr suitable for
standardization.
> Yes, I know boost::shared_ptr<> supports custom deleters, but as I
> said, I don't know why boost::scoped_ptr<> don't. Using shared_ptr is a
> possible solution but using it:
> 1. Gives the wrong impression that the resource is shared/sharable
> 2. Is probably heavier than the simpler scoped_ptr<>
>
> Adi
>
For a specific call an external function on destruction it has double
the overhead one the pointer and two a function pointer, in a specific
situation like call f(A*) to destroy it senario then neglecting threads
its simple to modify scoped_ptr.
off the cuff, this is almost boost::scoped_ptr<T> except for dtor and
additional data member.
template <class T>
class function_destroy_ptr
{
typedef void (*PF)(T *);
T *ptr;
PF destroy;
function_destroy_ptr(const function_destroy_ptr &);
function_destroy_ptr & operator = (const function_destroy_ptr
&);
public:
typedef T element_type;
function_destroy_ptr(T *a,PF b):ptr(a),destroy(b){}
~function_destroy_ptr() {(this->destroy)(ptr);}
T & operator *() const {return *ptr;}
T * operator ->() const {return ptr;}
T * get() const {return ptr;}
};
template <class T>
inline T get_pointer(function_destroy_ptr<T> const &x)
{
return x.get();
In the first case, the deleter could be given as one of the template
arguments, this way, it does not incure, asfar as I can tell, any size
overhead to the smart pointer, since the deleter can be created locally
in the deleting function or use a static operator().
raii_ptr<A,Deleter> p(new A);
and the impementation would be something like:
template <class T, class Deleter>
raii_ptr::~raii_ptr()
{ Deleter d;
d(ptr);
}
I think this would work even for bare functions.
In the second case, an actual deleter object must be created OUTSIDE
the deleting function and given to the smart pointer as an argument. In
this case, as you (and other suggested implementations on this thread)
say, the additional pointer WILL incur a size overhead for the smart
pointer.
I believe, that more often than not, and especially when using C APIs
with bare functions, the first case would be fitting.
I think scoped_ptr<> could easily support the first version without any
additional size overhead.
Adi
> IIRC, your second observation is also the reason. I believe
> shared_ptr stores the custom deleter along with the reference count on
> the heap. Adding custom deleter to scoped_ptr would double the size
> of the scoped_ptr itself and require an additional heap allocation.
> Conversely, the built-in overhead of shared_ptr "hides" the cost of
> the custom deleter.
But that stored deleter is there just to cover special cases, and many
programs don't need any of those. Then you can plug in any custom deleter
just as a static policy. With 0 overhead.
My smart pointer starts as
template<class TYPE, class DELETER = aptr_delete<TYPE> >
and reset works as
inline void reset(TYPE * P_ = 0)
{DELETER::Delete(ptr); ptr = P_;}
And I use deleters like this:
template<class TYPE>
struct aptr_delete {
typedef char type_must_be_complete[sizeof(TYPE)]; // boost trukk
inline static void Delete(TYPE * iPtr) { delete iPtr; }
};
template<class TYPE>
struct aptr_deleteArr {
typedef char type_must_be_complete[sizeof(TYPE)]; // boost trukk
inline static void Delete(TYPE * iPtr) { delete[] iPtr; }
};
template<class TYPE>
struct aptr_release {
inline static void Delete(TYPE * iPtr)
{
if(iPtr)
iPtr->Release();
}
};
struct aptr_cex { // deleter for CException
inline static void Delete(CException * iPtr)
{
if(iPtr)
iPtr->Delete();
}
};
Yes and no.
IIRC, the stored deleter isn't about "special cases" but rather is
necessary to achieve one of the design goals of shared_ptr: that the
allocation & deletion policy should not affect the type of the
shared_ptr itself.
Of course, since scoped_ptr is non-copyable, it's less likely to be
used as an interface type (such as a function parameter type or return
type), so the type-dependency may not matter so much. On the other
hand, it's not entirely out of the question that somebody might want
to write a function that takes or returns a reference to a scoped_ptr,
so maybe we do care about its suitability as an interface type.
The point is that there are trade-offs and alternatives to be
discussed, and the discussion -- to the best of my limited knowledge
-- just hasn't happened yet, because (a) most of the focus has been on
getting shared_ptr ready for standardization and (b) scoped_ptr has
hitherto been considered a specific-purpose type, not a generalized
resource guard.
> My smart pointer starts as
>
> template<class TYPE, class DELETER = aptr_delete<TYPE> >
>
Yep, a perfectly good implementation. (As long as we don't mind
mixing the deletion policy into the type of the smart pointer.) I
think the Loki smart_ptr could be parameterized to do basically the
same thing. (It's been a while since I looked at Loki, but this
sounds like some of the policy dimensions that it incorporated.)