Memory Allocator - Object Construction

Started by
5 comments, last by turch 12 years, 2 months ago
I've written a basic memory allocator.
It allocates a large chunk of memory once, then distributes portions of that memory throughout the program.

My problem is that when I distribute a chunk of memory for an object, I am simply passing it cleared memory.
The object itself is never being constructed.

This is fairly easy to solve by moving object construction into an Init function, but I feel like that is more
of a hack than a real fix. Objects of indeterminate types should be constructed upon instantiation.

For reference, here is an excerpt from the header file:

u8* Alloc( u32 _size, HEAP_POS _pos = HEAD, u32 _alignment = 8 );

// Example usage:
// Object* pObject = Alloc<Object>();
template<typename T>
T* Alloc( HEAP_POS _pos = HEAD, u32 _alignment = 8 )
{ return (T*)Alloc( sizeof(T), _pos, _alignment ); }

// Example usage:
// Object* pObject = Alloc<Object>( 5 );
// This will make a list of 5 objects.
template<typename T>
T* Alloc( u32 _elements, HEAP_POS _pos = HEAD, u32 _alignment = 8 )
{ return (T*)Alloc( sizeof(T)*_elements, _pos, _alignment ); }

//===
// Heap Functions
void AllocHeap( u32 _size );
void ClearMem();
void ReleaseMem();


Any ideas?

-Edit
It occurs to me that templated types have no way of knowing what constructors type T may have,
so I'm starting to think the solution is external.


-Edit 2
Did some more exploring and stumbled upon the placement new operator, which constructs an object at a specific place in memory:

Object* o = new(mem_location) Object();


Now I'm just trying to figure out a way to use this in a templated function, since a default constructor may not always be available.


-Edit 3 [Solution]


// Example usage:
// Object* pObj = Alloc<Object>();
// ComplexObject* pObj = Alloc<ComplexObject>( ComplexObject( args ) );
template<typename T>
T* Alloc( T& _constructor = T(), HEAP_POS _pos = HEAD, u32 _alignment = 8 )
{
T* memLoc = (T*)Alloc( sizeof(T), _pos, _alignment );
*memLoc = _constructor;
return memLoc;
}


The solution is to pass a reference to a constructor into the templated alloc function.
I then cast the memchunk and use the assignment operator to copy the object.
The reason I prefer this over memcpy is to give the programmer the flexibility
of defining how their object is copied via assignment operator.


The biggest drawback here is that if the user passes an object without a copy constructor
into the templated alloc function, the compiler will show the error in the file where the templated
alloc function is defined instead of where the problem occurs. For user-convenience and since this
is an easy problem to find, I left the default _constructor argument.
Advertisement
Mod - I accidentally posted this under Game Programming, could you please move it to General programming?

Thanks!
Also consider globally (or selectively for some classes) overloading the new, new[], delete and delete[] operators. That way you can provide your own memory allocation, the compiler will handle the constructors and destructors as normal, and you use the new/delete operators as normal in your code.
Now I'm just trying to figure out a way to use this in a templated function, since a default constructor may not always be available.[/quote]

A default constructor will always be available. If you do not write one yourself the compiler will generate it.
If using such allocator, then performance is at premium. Running constructors would only harm that, whether with default or user-specified parameters. Instead, use POD types.

Misaligned types on i5 and i7, at least recent models have no penalty. For SSE, complex classes or members that aren't primitive types aren't applicable either. For ARM or other non-x86 architectures, misaligned types cause all kinds of difficult problems and it needs to be maintained throughout entire structure, so a simple factory often won't suffice.

Now I'm just trying to figure out a way to use this in a templated function, since a default constructor may not always be available.[/quote]

In general, don't do it. It's the usual mess with going too far with abstractions over factory pattern.

Simple solution is this:// T is implied type, perhaps in outer class
template < class P1 >
T * Alloc(P1 p1) {
void * t = internal_alloc();
return new (t)T(p1);
}

template < class P1, class P2 >
T * Alloc(P1 p1, P2 p2) {
void * t = internal_alloc();
return new (t)T(p1, p2);
}
// and so on


Another option is copy-construction, which is what STL containers do. T * Alloc(const T & v) {
return new (t)T(v);
}
Note the potential overhead.

Now I'm just trying to figure out a way to use this in a templated function, since a default constructor may not always be available.


A default constructor will always be available. If you do not write one yourself the compiler will generate it.
[/quote]

I'm not always able to communicate what I mean properly, for that I apologize.
If you create a structure and define a parameterized constructor but no default constructor,
you should not be able to instantiate that object without parameteters.

Perhaps there is something I'm missing, though.

Here is some pseudo-code that exemplifies what I mean:


struct test
{
};
struct test2
{
test2(int x) {}
};
int main()
{
test t1;
test2 t2;
test2* t3 = new test2;
return 0;
}
Thanks everyone.

Brother Bob,
I am familiar with overriding the new and delete operators. It's a good suggestion since it simplifies your
memory allocator's interface. My current allocator is contained within an object, so a global new overload may
not work unless there is a global allocator. It just so happens that I have one based on this code, and at some
point I probably will implement this.


Antheus,
After doing some research and checking back, you and I seem to have come to the same solution.
I appreciate the feedback, your post was very informative.
I'm actually ok with a small performance hit from using a copy constructor. As this allocator does not
support deallocation, overall system performance should not take a real hit from using copy constructors.

That said, the non-templated alloc() function can be used in place of the templated version where appropriate,
as it will still simply return zero'd memory maintaining optimal allocation rates.
Ah, I see what you mean. In that case, your code will not compile smile.png

The compiler generates actual code for each specific template instantiation. It is (very roughly speaking) like the compiler simply replaces T with the actual class you give it, and then proceeds to compile normally.

This topic is closed to new replies.

Advertisement