Googles appar
Huvudmeny

Post a Comment On: cbloom rants

"11-06-12 - Using your own malloc with operator new"

11 Comments -

1 – 11 of 11
Blogger Arseny Kapoulkine said...

This does not work with multiple inheritance, i.e.

struct A { virtual ~A() {} char a; };
struct B { virtual ~B() {} char b; };
struct C: A, B { };

B* b = MyNew(C)();
MyFree(b);

As far as I know, unless you have RTTI and are willing to use dynamic_cast (ugh), you have to resort to overriding the operator delete, locally (inside the class) or globally. For global overrides, at one company we used a complicated system to be able to specify a per-allocation pool (i.e. MyNew(pool, type) + MyDelete(pool, ptr)) - it stored the pool pointer on a thread-local deallocation stack and then called the global operator delete, which inspected the stack to get the pool pointer (the stack is needed since MyDelete operations can cascade in case they are called from the dtor).

God, I hate C++.

November 7, 2012 at 10:17 AM

Blogger Arseny Kapoulkine said...

dynamic_cast was meant to be dynamic_cast < void* >

November 7, 2012 at 10:18 AM

Blogger Cat said...

If it really fails with MI, can you add a static_assert to detect MI use?

November 7, 2012 at 11:00 AM

Blogger cbloom said...

Yeah it definitely fails with MI. The problem is you need to be able to find the base allocation pointer from any of the types in the class hierarchy.

That didn't even occur to me cuz I just never use MI any more.

I believe it's fundamentally impossible to fix without adding meta-language.

The problem is when you are given a B* to free, there's nothing at compile-time that tells you that B* is a standalone object, or part of a multiple inheritance.

It's easy to fix with meta-language. Two possibilities :

1. Require that all polymorphic objects have a "get_base" virtual member function that provides the allocation address.

2. Make MyDeleteVirtual take an extra template parameter in which the caller explicitly specifies the base type.

3. The only time I've used MI in a large code base, we required that every polymorphic MI class derive from a virtual base Root class, so that you could always cast to Root to get a consistent address for an object.

4. When the destructors run, make them register their pointer on a stack.

5. (abusing the language) You should be able to grab the address of the destructor from your vtable and thus find your most-derived type.

It would be nice to at least detect MI at runtime and assert. (the underlying allocator can probably do this)

November 7, 2012 at 11:51 AM

Anonymous Anonymous said...

If you want to do any optimization of your allocator based on passing in the block size on free, but you don't want to HAVE to pass in the block size on free, don't you need to call a different malloc depending on whether you will free it with the block size or not? Or are you sucking up an O(N) free in the no-size case?

November 9, 2012 at 9:33 AM

Blogger cbloom said...

When no size is provided I do a hash table lookup, so technically O(1), but yeah.

I presume that small/non-polymorphic allocs are common (and should be fast and minimum overhead), and large/polymorphic are rare.

Surely it would be better to just have two completely different allocators, one for small blocks and one for large/polymorphic, and make the caller use a totally different malloc/free pair for those two cases.

In my personal code I like to do mallocs by reserving chunks of virtual address range, but I figured that was too messy in a library.

November 9, 2012 at 10:08 AM

Blogger   said...

new [], delete []?

November 12, 2012 at 5:53 AM

Comment deleted

This comment has been removed by the author.

November 12, 2012 at 5:53 AM

Blogger   said...

by the way (off topic) is there a way to get blogger to email you when somebody replies to you or comments on one of your blog posts?

November 12, 2012 at 5:54 AM

Blogger cbloom said...

" new [], delete []? "

Assuming non-polymorphic, those are straightforward.

It's up to you whether or not you want to shift the pointer and store the count before the head of the array. (that's needed if you want delete [] to work without passing in the count).

Personally I don't do that, I pass in the size to MyDeleteArray().

Actually I almost never new/delete arrays, if I have an array I just use some kind of template array container.

November 12, 2012 at 10:35 AM

Blogger cbloom said...

You use something like :

template < typename t_type >
static inline t_type * construct(t_type * pArray,const size_t size)
{
for(size_t i=0;i<size;i++)
new (ePlacementNew, pArray+i) t_type;

return pArray;
}

November 12, 2012 at 10:37 AM

You can use some HTML tags, such as <b>, <i>, <a>

This blog does not allow anonymous comments.

Comment moderation has been enabled. All comments must be approved by the blog author.

You will be asked to sign in after submitting your comment.