
From: "Matthew Hurd" <matt@finray.net>
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@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;