|
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