Boost logo

Boost :

From: Rob Stewart (stewart_at_[hidden])
Date: 2004-03-30 10:37:03


From: "Matthew Hurd" <matt_at_[hidden]>
>
> > Seeing the examples and code, I see that you're doing more than
> > just managing the deletion of such buffers; you providing the
> > means of allocation, the size, the data type, etc.
>
> Yep, why waste seconds with struct buf { size_t size; char * data; }
> when you can spend an order of magnitude longer making it pretty ;-)

8-)

>
> > Combining your create_refer with kill_nothing would be more
> > meaningful than either separately. Can you think of an occasion
> > when you'd want to allocate a buffer but not release it?
>
> The answer to this is, "I often do this". I use libraries where I must
> allocate and then forget about the buffer and I use libraries where I =
> must
> accept a buffer and destroy. I also use stuff where I must not delete =
> the
> data and I simply want to refer to an existing buffer. I've done this =
> with
> math/statistic, messaging and process group libraries recently.
>
> This means I want to optionally use the create and kill functions from =
> the
> storage policies. I could do this with=20
> 1. bools as template parameters,=20
> 2. constructor values with even run-time modifiable state, or,
> 3. via another policy-like thing.
>
> 1. is not preferred by me as have true, false or true, true in a =
> declarative
> thing is confusing to me. Could use macros or enums or static constants =
> to
> get around this I guess, but people will find a way around it ;-)

There are many things people can get around in C++.

> 2. need to store the vars in the structure violating my need for simple
> binary compatibility with popular buffer structs.

Yep. Not good.

> 3. The only one left is a policy-like thing, this could be two, one for
> allocation and one for deletion, like the flag thing, or just bung it =
> into
> one. One will do for me.

4. Provide common functionality that can be reused and assembled
   into policies. I'll give an example later.

> What about?
>
> struct storage_kill_only
> {
> BOOST_STATIC_CONSTANT(bool, kill =3D true );
> BOOST_STATIC_CONSTANT(bool, allocate =3D false );
> };
>
> struct storage_allocate_only
> {
> BOOST_STATIC_CONSTANT(bool, kill =3D false );
> BOOST_STATIC_CONSTANT(bool, allocate =3D true );
> };
>
> struct storage_no_allocation_or_kill=20
> {
> BOOST_STATIC_CONSTANT(bool, kill =3D false );
> BOOST_STATIC_CONSTANT(bool, allocate =3D false );
> };
>
> struct storage_allocate_and_kill
> {
> BOOST_STATIC_CONSTANT(bool, kill =3D true );
> BOOST_STATIC_CONSTANT(bool, allocate =3D true );
> };

This seems just too odd to me.

> > This is where I disagree with the flexibility. I think the
> > allocation and deallocation routines should be paired in a single
> > policy. That way, to create a new combination, one must do so
> > expressly, not accidentally as the current design allows.
>
> Paired is good, with the caveat that either one can be disabled.

I'd say paired is good, with the caveat that it is easy to create
the right pair.

> template < class T, std::size_t MaxSize >
> struct storage_policy_new
> {
> static T * create()
> {
> return new T[MaxSize];
> }
>
> static void kill( T * p )
> {
> delete [] p;
> }
> };

template < typename T, typename MaxSize >
struct allocate_array_new
{
   static T * create()
   {
      return new T[MaxSize];
   }

   template < typename SizeType >
   static T * create(T * source, SizeType size)
   {
      BOOST_ASSERT(size <= MaxSize);
      return static_cast<T*>(
         memcpy(create(), source, size * = sizeof(T)));
   }
};

template < typename T, typename MaxSize >
struct allocate_nothing
{
   static T * create()
   {
      return 0;
   }

   template < typename SizeType >
   static T * create(T * source, SizeType size)
   {
      return source;
   }
};

template < typename T, typename MaxSize >
struct deallocate_array_delete
{
   static void kill(T * p)
   {
       delete [] p;
   }
};

template < typename T, typename MaxSize >
struct deallocate_nothing
{
   static void kill(T *)
   {
   }
};

template < typename T, typename MaxSize >
struct storage_free_store_array
   : allocate_array_new
   , deallocate_array_delete
{
};

template < typename T, typename MaxSize >
struct storage_delete_array
   : allocate_nothing
   , deallocate_array_delete
{
};

Here I've created separate allocation and deallocation policies
and then created raw_buffer policies that combine them, giving
the combinations an appropriate name.

> template < class T, std::size_t MaxSize >
> struct storage_policy_null
> {
> static T * create()
> {
> return NULL;
> }
>
> static void kill( T * p )
> {
> =20
> }
> };

My version:

template <typename T, typename MaxSize>
struct storage_null
   : allocate_nothing
   , deallocate_nothing
{
};

> which is superfluous but it is perhaps nicer to look at=20
>
> data<2048,storage_policy_null, storage_no_allocation_or_kill>

   raw_buffer<2048,storage_null>

> > > {
> > > static T * create()
> > > {
> > > // shouldn't be called
> > > BOOST_STATIC_ASSERT(false);
> >=20
> > That will cause a compile-time assertion whenever this class is
> > instantiated, won't it? The only way that would be safe is if
> > create() were a function template that was only instantiated if
> > used. To prevent calling this function, why not omit it?
>
> Only if the default constructor is used, which it shouldn't have been, =
> so it
> could have been removed, but the assert message was cleaner to me.

I prefer to get my false values for such assertions by negating a
string literal. That way, the text appears in the assertion
output.

> Also I think data is probably a bad name, depends on the namespace =
> though.
> net::data works for my network lib, but perhaps raw_buffer is more =
> generic.

Yes. I like raw_buffer better.

> Thanks Rob. I've attached an improved version based on your comments
> and guidance. Thanks for your thoughtful comments.

You're welcome. I'll wait to look at the implementation until
after you consider these comments.

> The storage_allocate_and_kill bizzo is perhaps the most controversial
> aspect. I look forward to any comment w.r.t. this.

As you can see, I didn't like it.

> typedef T value_type;
> typedef T& reference;
> typedef const T& const_reference;
> typedef T* pointer;
> typedef std::ptrdiff_t difference_type;
> typedef SizeType size_type;
>
> typedef StoragePolicy<T,MaxSize> storage_policy;
> typedef OwnershipPolicy ownership_policy;
>
> typedef raw_buffer<MaxSize,StoragePolicy,OwnershipPolicy,T,SizeType> =
> this_type;

I think those will do nicely.

> // implicit conversion is a bad idea, but I'm addicted to its =
> handiness, someone slap me ;-)
> // Rob Stewart slapped me so it is out until I turn the other cheek, =
> or I'm hidden from sight ;-)

;-)

> // operator T * () { return raw_buffer_; }
>
> void Swap( this_type& d ) throw()

Why capitalized?

-- 
Rob Stewart                           stewart_at_[hidden]
Software Engineer                     http://www.sig.com
Susquehanna International Group, LLP  using std::disclaimer;

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