Re: Placement new[]
Am 30.01.2014 14:03, schrieb Joe The Smoe:
OK, I understand.
So, my implementation of the class pool is probably wrong. I intended to
put a pointer to the allocating pool object just before the allocated
block asked by a placement new/new[] in order to allow only code
modification by replacing the new operator by a it placement's
counterpart, keeping the delete invocation equal:
....
Couple of problems with this code, namely the alignment might not be
right. But if operator new[] and operator delete[] are implemented in
the same way, then *that* part should be correct. I'm a bit too lazy to
try your code with g++, but I'll just share mine if you want to take it.
Here's an implementation that works with g++: What I show here is the
class you need to inherit from to be able to "pool allocate" any
derived classes.
class JObject {
private:
// Make Operator New without arguments private here.
// They must not be used.
void *operator new[](size_t)
{
assert(false);
return (void *)1; // GNU doesn't accept NULL here. This should
throw as well.
}
void *operator new(size_t)
{
assert(false);
return (void *)1;
}
//
// Private structure for effective arena-new management:
//
// Due to bugs in both the GCC and the VS, we must keep the
// size in the memory holder as well. The size parameter of
// the delete operator is just broken (a little broken on GCC,
// a lot broken on VS).
// This is still slightly more effective than using AllocVec
// here.
// *BIG SIGH*
union MemoryHolder {
// This union here enforces alignment
// of the data.
struct {
// back-pointer to the environment
class Environ *mh_pEnviron;
size_t mh_ulSize;
} mh;
// The following are here strictly for alignment
// reasons.
union Environ::Align mh_Align;
};
//
public:
// An overloaded "new" operator to allocate memory from the
// environment.
static void *operator new[](size_t size,class Environ *env)
{
union MemoryHolder *mem;
// Keep the memory holder here as well
size += sizeof(union MemoryHolder);
// Now allocate the memory from the environment.
mem = (union MemoryHolder *)env->AllocMem(size);
// Install the environment pointer and (possibly) the size
mem->mh.mh_pEnviron = env;
mem->mh.mh_ulSize = size;
return (void *)(mem + 1);
}
//
static void *operator new(size_t size,class Environ *env)
{
union MemoryHolder *mem;
// Keep the memory holder here as well
size += sizeof(union MemoryHolder);
// Now allocate the memory from the environment.
mem = (union MemoryHolder *)env->AllocMem(size);
// Install the environment pointer and (possibly) the size
mem->mh.mh_pEnviron = env;
mem->mh.mh_ulSize = size;
return (void *)(mem + 1);
}
//
//
// An overloaded "delete" operator to remove memory from the
// environment.
static void operator delete[](void *obj)
{
if (obj) {
union MemoryHolder *mem = ((union MemoryHolder *)(obj)) - 1;
mem->mh.mh_pEnviron->FreeMem(mem,mem->mh.mh_ulSize);
}
}
//
//
static void operator delete(void *obj)
{
if (obj) {
union MemoryHolder *mem = ((union MemoryHolder *)(obj)) - 1;
mem->mh.mh_pEnviron->FreeMem(mem,mem->mh.mh_ulSize);
}
}
};
The "Environment" is what your pool is. It provides functions AllocMem()
and FreeMem() that handle memory. Both require a pointer and a size. It
also contains a small union of the following type:
union Align {
UBYTE a_byte;
UWORD a_word;
ULONG a_int;
FLOAT a_float;
DOUBLE a_double;
UQUAD a_quad;
APTR a_ptr;
};
that is just there to ensure correct alignment of all types. The UPPER
CASE names there are environment specific types and set accordingly,
using autoconf.
The construction works fine with g++, and VS, though a couple of bugs
were present that required me to keep the size in the allocation itself
because the compiler provided value was, for some versions of the
compilers, incorrect.
To use it, first derive from JObject:
class bla : public JObject {
...
}
and then:
foo = new(environ) bla(...);
delete foo;
does what it should do, including arrays (tested). If it does not,
you're mixing new[] with delete, or new with delete[].
HTHH,
Thomas