E. Gladyshev wrote: > I am using STL and trying to use boost in my daily > development. What can I do to implement consistent classes in terms > of memory management. For example, if I need to implement a class A > that has a container and pointer to a data type. I have two options: > > template< typename T > > struct A > { > shared_ptr<T> _data; > std::list<T> _list; > }; > > template< typename T, template A = std::allocator<T> > > struct A > { > shared_ptr<T> _data; > std::list<T, A> _list; > A : _data( new T() ) {...} > }; > > The problem with the first defenition is that I am limiting the > built-in STL functionality in terms of memory management. > > The problem with the second definition is that if I expose the > allocator template parameter, the user of my class will expect that > all memory allocations of type T are going to be using her allocator > but boost::shared_ptr doesn't support it.
It does... to an extent. You can't customize the count allocations, but they aren't T allocations. For the T allocation you'll need something along the lines of: template<class A> struct X { typedef typename A::value_type T; static T * create(A & a) { T * p = a.allocate(1); try { new(p) T; } catch(...) { a.deallocate(p, 1); throw; } return p; } static void destroy(A & a, T * p) { p->~T(); a.deallocate(p, 1); } }; The above doesn't respect A::pointer etc. Now you can _almost_ do the straightforward thing: template< class T, class A = std::allocator<T> > struct Y { shared_ptr<T> _data; std::list<T, A> _list; Y(): _data( X<A>::create(), X<A>::destroy ) {...} }; except that X<A>::create and ::destroy take a reference to an allocator. This should remind you that you forgot the allocator argument in the constructor: Y(A const & a = A()): ... Now _list will make itself a copy of that 'a'. This immediately hints at a potential problem: template< class T, class A = std::allocator<T> > struct Z { std::list<T, A> _list; std::list<T, A> _list2; }; where _list and _list2 will make their own copies and can't be made to share a single allocator instance. But let's ignore that (presumably the author of a stateful A will use a <drumroll> shared_ptr<> underneath so that copies use the same heap) and get back to our list+shared_ptr. Now who should own the allocator that is used to construct or later destroy *_data? Should we use _list.get_allocator() for that? We can't since a copy of Y will copy _list but share _data, and the original may get destroyed along with the allocator. So we'll need to do something along the lines of: Y(A const & a = A()): _list(a), _data() { A a2(a); // original is const T * p = X<A>::create(a2); // evaluation order _data.reset( p, bind(X<A>::destroy, a2, _1) ); } Not very pretty, but that's what you need to pay for being std::allocator-based; its interface is tailored for containers. For such a case I'd definitely consider going back to template< typename T > struct A { shared_ptr<T> _data; std::list<T> _list; }; unless there are very solid reasons for the allocator parameter. ;-) Or you can write your own alloc_ptr<T, A>, of course, if you want to go the allocator route; it is quite a different beast compared to shared_ptr as it doesn't support polymorphism, for example, so it deserves its own name. But your code still looks somewhat artificial to me; why would you want to deep copy _list but shallow copy _data? It doesn't make much sense. _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost