Re: Custom allocator trouble
On May 25, 12:24 pm, Dizzy <d...@roedu.net> wrote:
Hello
Because my allocation needs are very basic I do not
think I should deal with the complexity of writting a general allocator (ie
an allocator that may allocate chunks of various sized bytes alligned for
various types) but a simple one that only needs to allocate space for a
single value of a known type throughout the allocator lifetime.
The "allocate chunks" part of the code should not know anything about
alignment, other than it returns a max aligned pointer on every call.
Classes and primitives that handle arrays will handle the alignment
part for itself.
To use a class like your "SimpleAllocator" you would do this
(Stroustrup mentions this technique in D&E)
struct SimpleAllocator{
//class "owns" a region of memory
//determined when class is constructed
SimpleAllocator(void* data, size_t len );
void* allocate(size_t n);
void deallocate(void*);
size_t max_size()const;// max contiguous block
bool operator==(SimpleAllocator const&)const;//for use
later
};
Now make a helper class
struct SimpleAllocHelper{
void * operator new(size_t n,SimpleAllocator&s){
char* r=reinterpret_cast<char*>(s.allocate(n
+sizeof(SimpleAllocator*) );
void**alloc=&(r);
alloc=&s;//store for later use
return r+sizeof(SimpleAllocator*) ;
}
//for exceptions
void operator delete(void* p,SimpleAllocator&s){
char* r=reinterpret_cast<char*>(p);
void adj=r-sizeof(SimpleAllocator*);
s.deallocate(adj);
}
void operator delete(void* p){
if(!p)return;
char* r=reinterpret_cast<char*>(p);
void adj=r-sizeof(SimpleAllocator* );
SimpleAllocator*a=reinterpret_cast<SimpleAllocator*>(adj);
a->deallocate(adj);
}
private:
void * operator new(size_t);//no access to default operator new
};(implement same logic for array forms of new/delete
Now,we can do this
struct Info:SimpleAllocHelper{
int data;
};
If everything in Info is a POD type, then this works fine. In other
words, if
union PODcheck{Info v;};
compiles, this works.
Now I can write
char buf[100000];
SimpleAllocator a(buf,sizeof(buf));
//std::auto_ptr<Info>i(new Info);// does not compile
std::auto_ptr<Info>i(new (a) Info); //this will compile
It is also possible to make a STL allocator that wraps
SimpleAllocator, and you can use it in containers.
This technique is known as "private heaps" or "stateful allocators."
In other words, each allocator object is initialized with the region
it manages, as opposed to using static initialization. Of course, you
don't even need SimpleAllocHelper, you can just use palcement new. But
then you cant use delete, and by extention auto_ptr, and so on. And it
gets even more complicated when Info is not a POD type.
stateful allocators is perhaps the most cumbersome of all the
allocators models. You will spend most of your time figuring out how
to make sure every object that needs to use a particular memory region
actually does.
I have worked on system that attempted to use this method of placing
object in shared memory: it was a disaster.
What you really want to do is make SimpleAllocator have a default
constructor, ie. make it a Singleton, or, have nothing but static
methods. Initialize the statics to your shared memory region before
first use. THIS is the simplest allocator. Then make class specific
new/delete point to it just like you see in the Meyers books. And to
boot, cut and paste your STL implementation std::allocator , rename
it, and change whre it gets its memory from. This is a 10 minute job
at most.
Then you can do this:
struct SimpleAllocator2{
static void* allocate(size_t n);
static void deallocate(void*);
static size_t max_size() ; // max contiguous block
};
Make a helper
struct SimpleAllocHelper2{
void * operator new(size_t n){
return SimpleAllocator2::allocate(n);
}
void operator delete(void* p){
if(!p)return;
SimpleAllocator2::deallocate(p);
}
};(implement same logic for array forms of new/delete
//cut,paste, rename and modify std::allocator
template<class T> SimpleAlloc2::allocator;
Now, I can write
typedef
std::basic_string<char,char_traits<char>,SimpleAlloc2::allocator<char>
shstring;
struct Info2:SimpleAllocHelper2{
int data;
shstring stringdata;
};
std::auto_ptr<Info2> j(new Info2); //works, and is simpler
All we need to be careful about is that things that use SimpleAlloc2
should not be made into global or function statics. Global, because we
have not initialized the shared region yet, and function, because when
the process exits, it will try to free the shared memory, and it may
have already been detached.
Now you are just left with implementing the free list manager
(SimpleAllocator2) itself. There are TONS of algorithms to do this out
there. I would just buy the DinkumWare Allocator Library and just
piece together what you need. Or there are other free list managers
available for download.
Lance
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]