Arlo Belshee on 2 Jul 2003 20:11:01 -0000 |
[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]
[ALACPP] Code for smart pointer to separate memory management strategy from allocator |
Here's the code that I was thinking of in my last post. Please rip to pieces. ;) /* allocators are policies, which meet the following interface. They simply allocate & deallocate memory; they don't construct / destroy objects. */ class DefaultAllocator { void* allocate(size_t s); void deallocate(void *p, size_t s); }; // The managed ptr header: namespace detail { template< class _Allocator, int size > struct Destructor { void operator()(void *obj) { _Allocator.deallocate(obj, size); } }; } template < typename _T, template< typename > _MemoryManagementPtr = boost::shared_ptr, class _Allocator = DefaultAllocator > class CustomManagedPtr : public _MemoryManagementPtr< _T > { // c'tors, op= & d'tors that match those for boost::shared_ptr, // and just inline a call to the base. typedef detail::Destructor< _Allocator, sizeof(_T) > Dest; public: void Create() { void *mem(alloc_->allocate(sizeof(_T))); reset(new (mem) _T, Dest); } template< typename _Arg1T > void Create(_Arg1T arg1) { void *mem(alloc_->allocate(sizeof(_T))); reset(new (mem) _T(arg1), Dest); } // etc. // Actually, this template family would probably be built using Boost PP, // so that we could simply control the max number of c'tor args with // a single constant. private: MonostatePtr< _Allocator > alloc_; }; // Use: // In headers where you select how you want to alloc things. template< typename _T, template< class > _MemManagementStrategy = boost::shared_ptr > struct DefaultMemModel { typedef CustomManagedPtr< _T, _MemManagementStrategy, MyFunnyAllocator > type; }; // specializations, if you want them. // defined in some other library: void DoStuff(boost::shared_ptr< Foo > f); // At point of use: typedef DefaultMemModel< Foo, boost::shared_ptr >::type FooPtr; typedef DefaultMemModel< Foo, boost::scoped_Ptr >::type ScopedFooPtr; typedef CustomManagedPtr< Foo, boost::shared_ptr, MyAllocatorUsedOnlyHere > SpecialFooPtr; FooPtr f; f.Create("asdf", 10); ScopedFooPtr f2; f2.Create(); SpecialFooPtr f3; f3.Create(*f); // copy of f's foo, but allocated with different allocator. DoStuff(f); DoStuff(f3); // The above code would look like the following with // plain shared pointers instead: typedef boost::shared_ptr< Foo > FooPtr; typedef boost::scoped_ptr< Foo > ScopedFooPtr; FooPtr f; f.reset(new Foo("asdf", 10)); ScopedFooPtr f2; f.reset(new Foo); FooPtr f3; f.reset(new Foo(*f)); DoStuff(f); DoStuff(f3); // Done. Same except for the typedefs & the use of a Create instead of new Foo. However, it allows custom allocators trivially. In particular, it moves the selection of allocation method from the invocation to the typedef. Sometimes I want to choose my allocator at time of use, but most often it will be determined by my overall memory management strategy, and it's nice to be able to wrap that up into a pointer typedef. Note that this solution only works because boost has stated as part of their spec that shared_ptr & scoped_ptr do not take any additional default template args. Thus, I can use the template template parameter. Of course, I could get the same behavior with a type computer as a policy, if I had to. It's just not as pretty. In general, the cusom management ptr is written to the union of the interface of scoped_ptr & shared_ptr (also scoped_array & shared_array?), depending on bad template instantiations to find errors in use. The whole system works because the smart pointers take destructor functors at the same time as they do the object that they are to manage. Then, the type of the fucntor is lost to the compile-time system, and recovered via an internal virtual function call at runtime. Thus, we only need the extended type info at the time that we construct the smart pointer. So I simply inherit from the smart pointer, and override only things that happen prior to object construction. The fact that the object will be clipped when it is passed around is irrelevant: the correct d'tor will be called. Also, it is not a problem to reset the shared pointer to point to a different object, even if that object was allocated from a different allocator. For example: { shared_ptr< Foo > f2 { FooPtr f; f.Create(); f2.reset(f); } f2.reset(new Foo); } Because any call to reset changes not only the object referenced, but the destructor that will be called upon release, the reset in the last line performs the following sequence: 1) Use the destructor that was set by create to destroy the first instance of Foo, as it has now hit a ref count of 0. IE, call SpecialAllocator.deallocate(args). 2) Change the object & allocator stored in f2 to be the new object, and the default checked_delete (using delete). Thus, when f2 goes out of scope, it will delete the second instance using the default heap, just as if it had never been involved with the special allocator. Method of deletion is determined purely by method of creation, not by any intermediate type, as it should be. Arlo _______________________________________________ alacpp mailing list alacpp@xxxxxxxxxxx http://lists.ellipsis.cx/mailman/listinfo/alacpp