Boost logo

Boost :

From: Sven Van Echelpoel (sven.vanechelpoel.sv_at_[hidden])
Date: 2002-04-30 03:33:01


beware: lengthy post :-)

[...]

> But now another thought occured to me, which again turns the argument
> balance the other way in my mind. (This idea also answer Sven's post.) If
> the shared resources are OS resources, then templates are usually not
> necessary, because OS resource types are in limited number, so a complete
> (regular) typedef would do. How, then, about something like:

> typedef smart_ptr<HANDLE, Win32HandleDuplication, allow_conversion,
> assert_check, FileStorage>
> ResFile;

> typedef smart_ptr<HANDLE, Win32HandleDuplication, allow_conversion,
> assert_check, FileMappingStorage>
> ResFileMapping;

> typedef smart_ptr<HBRUSH, Win32GdiDuplication, allow_conversion,
> assert_check, GdiStorage>
> ResBrush;

> typedef smart_ptr<HBRUSH, Win32BitmapDuplication, allow_conversion,
> assert_check, GdiStorage>
> ResBitmap;

> Then, you'd use the ResXxx throughout. I guess you'd agree that the above
> is
> not much worse off than:

> typedef smart_resource<HANDLE, Win32FileAlloc>
> ResFile;

> typedef smart_resource<HANDLE, Win32FileMappingAlloc>
> ResFileMapping;

> typedef smart_resource<HBRUSH, Win32GdiAlloc>
> ResBrush;

> typedef smart_resource<HBRUSH, Win32GdiAlloc, Win32BitmapDuplication>
> ResBitmap;

> Eh? Whaddaya think?

> Andrei

Looking at the above typedefs there seems to be little difference
between smart_ptr and smart_resource. I'm a bit worried about the
proliferation of types when using smart_ptr to handle common resources.
Especially if you take the required interfaces of storage_policy and
ownership_policy into account, which you'd have to code separately (I
mentioned in my earlier post that is probably would be possible to
create some sort of "generic" policies that simply forward the resource
destruction and/or cloning/copying to the relevant API functions, which
would lessen the workload).

I must confess that my objection to Loki::SmartPtr is somewhat
theoretical, since I cannot use it one the compiler we use (MSVC6,SP5).
I liked the ideas in Loki::SmartPtr a lot and tried to roll my own
"smart_resource", taking compiler deficiencies into account, while
trying to incorporate as many of Andrei's concepts as possible. I'm not
a library writer - only a user/abuser - so what I came up with will
suffer from a range of problems I'm currently oblivious to, but it works
for the projects I work on and and the resources I work with.

Noticing that the two most variable pieces of functionality are the
destruction and the cloning/copying of the resource I factored these out
into a traits class. Even so most of the boilerplate code fits into a
basic_resource_traits class. This is more or less its definition (some
types and operator<<(ostream&) were omitted for brevity):

template<typename T, T DEF>
struct basic_resource_traits
{
 typedef T HandleType;
 typedef T ConstHandleType;
 typedef T* PointerType;

 // The default value of the resource 'handle'
 static HandleType NullValue;
 // Swaps two resource handles
 static void swap( ReferenceType l, ReferenceType r )
 {
   std::swap( l, r );
 }

};

template<typename T, T DEF>
BasicResourceTraits<T,DEF>::HandleType
BasicResourceTraits<T,DEF>::NullValue = DEF;

For any resource, I simply derive my traits class from
basic_resource_traits. Here's an example for HANDLE (used in a lot of
APIs):

// Most of the HANDLEs c
struct WinHandleTraits :
 public basic_resource_traits<HANDLE,NULL>
{
 // Destroys the handle
 static void destroy( HANDLE handle )
 {
        ::CloseHandle( handle );
 }
 // Clones the handle
 static HANDLE clone( HANDLE handle )
 {
  HANDLE currentProcess = ::GetCurrentProcess(); // never close
                                                 // this HANDLE
  HANDLE dup = NULL;

  if ( FALSE == ::DuplicateHandle( currentProcess, handle,
       currentProcess, &dup, 0, FALSE, DUPLICATE_SAME_ACCESS ) )
  {
     throw ResourceError( "Cannot duplicate HANDLE" );
  }

  return ( dup );
 }
};

And the corresponding typedef:

typedef deep_copy_resource<WinHandleTraits,AllowConversionPolicy>
StdHandle;

similarly I have a EventHandleTraits, defined like this:

struct EventHandleTraits :
 public basic_resource_traits<HANDLE,NULL>
{
 // Destroys the handle
 static void destroy( HANDLE handle )
 {
   ::DeregisterEventSource(handle);
 }
};

and the corresponding typedef:

typedef scoped_resource<EventHandleTraits,AllowConversionPolicy>
EvLogHandle;

I'm quite convinced this results in less types and keystrokes (yep, this
is nitpicking :-) ) and doesn't suffer from the one-definition problem
cited by Andrei in his earlier post, since I do not specialize the
traits for my handle types (I couldn't if I wanted to, since both
resources type use HANDLEs). But again, this works for the project(s)
I'm currently working on and it will not meet all the requirements for a
generic smart_ptr (b.t.w. I'm still using boost::scoped_ptr and
boost::shared_ptr for all/most of my free store allocated objects).

I guess what I'm trying to say is that although Andrei's typedefs seem
quite similar, actually they're not. I'd still prefer a separate
smart_resource over smart_ptr for the reasons outline above. On the
other hand I do agree that one should try to limit the number of "smart
pointers" out there.

There, I've said it.

Svenne.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk