Jump to content
  • Advertisement
Sign in to follow this  
SeiryuEnder

Memory Allocator - Object Construction

This topic is 2512 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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.

Share this post


Link to post
Share on other sites
Advertisement
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

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;
}

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!