views:

135

answers:

5

I made a couple macros to make using placement new a bit easier. I was just wondering if there were any obvious cases where these would not work. Thanks.

#define CONSTRUCT_INPLACE(TYPE,STORAGE,INIT)   ::new((TYPE*)STORAGE) TYPE INIT
#define DESTRUCT_INPLACE(TYPE,STORAGE)         ((TYPE*)STORAGE)->~TYPE()
A: 

Looking at them, I wonder that that first (TYPE*) cast is for - placement new takes a void*, after all.

But then again, I wonder what they're good for at all. All they manage is to further obfuscate something that isn't really trivial to begin with. Why do you think you need them?

That being said, have you looked at the (admittedly few) standard library facilities for dealing with uninitialized memory? (See the lower half of this website. There might be something you can use.

sbi
A: 

Your CONSTRUCT_INPLACE seems a bit odd to me. Usage would be:

void* p = <some memory location>;
Foo* f = CONSTRUCT_INPLACE(Foo, p, Foo())

// The macro-free version seems easier to read:
Foo* f = new(p) Foo();

DESTRUCT_INPLACE is at least easier to read than the code it replaces.

But really, why are you doing so much placement new that these macros are of use to you? Put that stuff in a pool class and forget about it.

David Seiler
Actually, it would be used like this: Foo* f = CONSTRUCT_INPLACE(Foo, p, () )
Adisak
+6  A: 

I'm not an expert in placement new but there are a couple of issues with how you are defining the macro.

Issue 1

The most obvious problem is the use of the cast (TYPE*)STORAGE for the storage location. This is incorrect. Placement new is just another C++ function and it participates in operations like overload resolution. Arbitrarily casting the memory to a specific type could cause the placement new to bind to a different operator new that the user expected.

For example, it's valid to have the following two definitions of placement new. Your macro would potentially cause the wrong one to be called.

void * _cdecl operator new(size_t cbSize, void* pv);
void * _cdecl operator new(size_t cbSize, SomeType* pv)-

...

// These two call different overloads
void* p = malloc(sizeof(SomeType));
SomeType* f1 = CONSTRUCT_INPLACE(SomeType, p,()) 
SomeType* f2 = new (p) SomeType();

I wrote a blog post awhile back on how you can use this type of overload resolution to implement custom allocators.

Issue 2

The expression STORAGE in the macro should be wrapped in parens to prevent evil macro expansion bugs.

::new((TYPE*)(STORAGE)) TYPE INIT
JaredPar
A: 

Placement-new operator creates an object in what initially is a block of "raw" memory. I.e. the proper type for the memory pointer passed to the placement new is 'void*'. Casting it to type '(TYPE*)', as you do, is harmless, but useless. What is the point of the cast in your 'DESTRUCT' macro is also not immediately clear.

BTW, what is the point of having these macros at all? You say they "make using placement new a bit easier", but all they seem to do is perform some completely unnecessary casts.

AndreyT
The storage block is a chunk of bytes, I cast it to the TYPE pointer so I can call the destructor. This is for some ugly Early Initialization code on a custom hardware system.
Adisak
If you can call the destructor, it means that the object has been previously constructed. If it has been constructed, you should see it through a typed pointer already - the very pointer returned to you by your placement-new macro. Of course, if you "lost" that pointer somehow, then you can call the destructor on "a chunk of bytes"...
AndreyT
+2  A: 

In addition to issues stated elsewhere your macros interact very badly (i.e. fail to compile) with template types.

In general, macros should be avoided when there is no clear benefit. To be brutally honest, I can't see a real benefit here.

e.g.

template< class A, class B >
class T
{
public:

    T(A y, B z) : x(y), w(z) {}
    A x;
    B w;
};

int main()
{
    void* p = ::operator new(sizeof(T));

    CONSTRUCT_INPLACE(T<int, double>, p, (4, 5.0));

    DESTRUCT_INPLACE(T<int, double>, p);

    ::operator delete(p);

    return 0;
}
Charles Bailey
Yes, good point. I can still use them but I have to typedef the templates and pass in the typedef rather than the template.
Adisak