Um, hello. I'm trying to write a custom stack memory allocator to help with my game programming, and have run into an issue. So, let's say my allocator has a char* buffer, and I want to get some memory for an int:
class MemoryStack
{
public:
MemoryStack(unsigned size)
{
mSize=size;
mTop=0;
mBuffer = new(std::nothrow) char [size];
}
char* allocate(unsigned size)
{
if (mTop + size > mSize)
return nullptr;
char* out = mBuffer+mTop;
mTop+=size;
return out;
}
private:
char* mBuffer;
unsigned mTop;
unsigned mSize;
};
int main ()
{
MemoryStack stack(1024);
int testval = 6;
int* ptr = (int*)stack.allocate(sizeof(int));
*ptr = testval;
std::cout<<*ptr;
}
Now, this works, and prints out 6. However, when I try something like:
int main ()
{
MemoryStack stack(1024);
std::string str = "HELLO :p";
std::string* strptr = (std::string*)stack.allocate(sizeof(std::string));
*strptr = str ;
std::cout<<*strptr;
}
...This gives me a Bad Ptr problem, and a segmentation fault, crashing my program. Can anyone explain why this is? Could this be because of some operator = overloading? And would there be any way to safely handle this? Thanks!
Edit: The following code is my currently final implementation after the help I received - it's not thoroughly tested, but seems to work as intended. I'll naturally modify it if any bugs are found, but as to help out anyone interested, here it is ^_^ Feel free to use it if you wish, although it's hardly the summit of computer science.
class MemoryStack;
/**Serves as a bookmark for the memory stack in order to allow to clear only part of the memory.*/
class MemoryBookmark
{
private:
/**Private constructor may only be called by the memory stack object.*/
MemoryBookmark(unsigned value)
{
mBookmark = value;
}
unsigned mBookmark;
public:
friend class MemoryStack;
/**Returns the index of the position that will be the new stack top pointer.*/
decltype(mBookmark) getValue() const
{
return mBookmark;
}
};
/**Acts as a basic memory stack to help reduce allocation costs, as well as add to the fun! Use with care, as destructors must be called manually.*/
class MemoryStack
{
private:
char* mBuffer;
size_t mTop;
size_t mCapacity;
size_t mAlignment;
public:
/**Initialises the class, reserving _capacity_ bytes for use. It can not be resized for efficiency purposes.*/
MemoryStack(unsigned capacity)
{
mCapacity = capacity;
mTop = 0;
mBuffer = new(std::nothrow) char[capacity];
mAlignment = 4;
}
/**Frees the memory, invalidating all internal memory. Doesn't call destructors.*/
~MemoryStack()
{
if (mBuffer)
delete[] mBuffer;
}
/**Creates an instance of the given type with Args if possible, using aligned internal memory.*/
template <typename T, typename... Args>
void create(T*& ptr, Args&&... args)
{
ptr = (T*)allocate(sizeof(T));
if (!ptr)
return;
else
new (ptr)T(std::forward<Args>(args)...);
}
/**Calls the destructor of the pointer. Must be used if destruction important.*/
template<typename T>
void destroy(T* ptr)
{
ptr->~T();
}
/**Allocates a piece of memory for use.*/
void* allocate(size_t amount)
{
size_t bt = (size_t)(mBuffer + mTop);
size_t alignoffset = mAlignment - (bt & (mAlignment - 1));
alignoffset = alignoffset == mAlignment ? 0 : alignoffset;
size_t size = amount + alignoffset;
if (size + mTop > mCapacity)
return nullptr;
else
{
mTop += size;
return (void*)(bt + alignoffset);
}
}
/**Returns the amount of memory used.*/
size_t size() const
{
return mTop;
}
/**Returns the size of the memory reserved for use.*/
size_t capacity() const
{
return mCapacity;
}
/**Returns the number of bytes remaining for allocation.*/
size_t remaining() const
{
return mCapacity - mTop;
}
/**Checks whether the internal memory was allocated successfully.*/
bool isValid() const
{
return mBuffer != nullptr;
}
/**Creates a 'bookmark' which can be used to clear the stack until a given point.*/
MemoryBookmark createBookmark() const
{
return MemoryBookmark(mTop);
}
/**Resets the stack. All data inside may now be overwritten. Doesn't call destructors.*/
void reset()
{
mTop = 0;
}
/**Resets the stack up to a given bookmark. Again, no destructors called!*/
void resetToBookmark(const MemoryBookmark bookmark)
{
mTop = bookmark.getValue();
}
/**Sets the alignment of the reservations in memory.*/
void setAlignment(size_t alignment)
{
mAlignment = alignment;
}
/**Returns the currently used alignment.*/
decltype(mAlignment) getAlignment() const
{
return mAlignment;
}
};
/**Test class.*/
class Test
{
public:
Test(int val)
{
v = val;
std::cerr << "Constructor\n";
}
~Test()
{
std::cerr << "Destructor";
}
int v;
};
/**Test it! XD*/
int main()
{
using namespace std;
{
MemoryStack stack(4096);
Test* test=nullptr;
int* i1, *i2;
char* c1, *c2;
stack.create(test,3);
stack.create(i1, 2);
stack.create(c1, 'a');
stack.create(i2, 3);
stack.create(c2, 'm');
stack.destroy(test);
stack.reset();
}
cin.get();
}