char array as storage for placement new
Asked Answered
H

5

9

Is the following legal C++ with well-defined behaviour?

class my_class { ... };

int main()
{
    char storage[sizeof(my_class)];
    new ((void *)storage) my_class();
}

Or is this problematic because of pointer casting/alignment considerations?

Hatshepsut answered 3/1, 2011 at 8:47 Comment(9)
For me, it is just fine.Astera
No, the ellipses are not legal in that context...(Hint: if you want to ask if code is well-defined, it has to compile first.)Lissa
Any one please tell me what is the usage of doing above thing in real pogramming world.Inhalant
@GMan: I really hope you're just joking. Yes, the well-definedness depends on the omitted section, but it's obvious what the question is.Granth
@Jon: No, not really joking. It's best if the question is clear, without ambiguity. (And no, whether it's well-defined does not depend on the omitted section; you simply have no such guarantee.)Lissa
i'd just love to know why anyone would want to do thisBryonbryony
@user430294 @David Heffernan: Herb Sutter wrote a GotW article (#28) about a pimpl idiom that uses something like what the OP wrote. Then he recommends against it because the array is not guaranteed to be aligned, unlike dynamic memory. In short: don't do it.Remde
@user430294 @David Heffernan: the goal would basically be to allocate the storage for an object but construct the object only as necessary. And at the same time preserving a cache-friendly memory access pattern (so no dynamic allocation).Hatshepsut
@Hatshepsut right, 'cos it wouldn't do to use dynamic allocation!Bryonbryony
L
15

Yes, it's problematic. You simply have no guarantee that the memory is properly aligned.

While various tricks exist to get storage with proper alignment, you're best off using Boost's or C++0x's aligned_storage, which hide these tricks from you.

Then you just need:

// C++0x
typedef std::aligned_storage<sizeof(my_class),
                                alignof(my_class)>::type storage_type;

// Boost
typedef boost::aligned_storage<sizeof(my_class),
                        boost::alignment_of<my_class>::value>::type storage_type;

storage_type storage; // properly aligned
new (&storage) my_class(); // okay

Note that in C++0x, using attributes, you can just do this:

char storage [[align(my_class)]] [sizeof(my_class)];
Lissa answered 3/1, 2011 at 9:10 Comment(3)
Sorry, not quite there yet :) As far as I've understood, the storage type is some kind of builtin integer type. How do I use it? Can I just cast freely to the my_class type via void *?Hatshepsut
Sorry, I meant to say: "cast freely back and forth to/from pointers to my_class instances"... Or something like that :)Hatshepsut
@bluescarni: Ah, sorry. My answer is incomplete: the resulting type should be used as a block of memory, so it should be the address of that storage. I've fixed it.Lissa
H
3

As people have mentioned here, this won't necessarily work due to alignment restrictions. There are several ways to get the alignment right. First, if you have a C++0x-compliant compiler, you can use the alignof operator to try to force the alignment to be correct. Second, you could dynamically-allocate the character array, since memory from operator new is guaranteed to be aligned in such a way that anything can use it correctly. Third, you could try storing the character array in a union with some type that has the maximum possible alignment on your system; I believe that this article has some info on it (though it's designed for C++03 and is certainly not as good as the alignof operator that's coming out soon).

Hope this helps!

Harmonium answered 3/1, 2011 at 9:11 Comment(0)
P
2

It is at least problematic due to alignment.

On most Non-Intel architecture the code will generate a "bus error" due to wrong alignment or be extremely slow because of processor traps needed to fix the unaligned memory access.

On Intel architecture this will normally just be a bit slower than usual. Except if some SSE operations are involved, then it may also crash.

Pavonine answered 3/1, 2011 at 9:5 Comment(0)
S
2

In case anyone wants to avoid Boost or C++1x, this complete code works both in GCC and MSVC. The MSVC-specific code is based on Chromium's aligned_memory.h. It's a little more complex than the GCC version, because MSVC's __declspec(align(.)) only accepts literal alignment values, and this is worked around using template specialization for all possible alignments.

#ifdef _MSC_VER

template <size_t Size, size_t Align>
struct AlignedMemory;

#define DECLARE_ONE_ALIGNED_MEMORY(alignment) \
template <size_t Size> \
struct __declspec(align(alignment)) AlignedMemory<Size, alignment> { \
    char mem[Size]; \
};

DECLARE_ONE_ALIGNED_MEMORY(1)
DECLARE_ONE_ALIGNED_MEMORY(2)
DECLARE_ONE_ALIGNED_MEMORY(4)
DECLARE_ONE_ALIGNED_MEMORY(8)
DECLARE_ONE_ALIGNED_MEMORY(16)
DECLARE_ONE_ALIGNED_MEMORY(32)
DECLARE_ONE_ALIGNED_MEMORY(64)
DECLARE_ONE_ALIGNED_MEMORY(128)
DECLARE_ONE_ALIGNED_MEMORY(256)
DECLARE_ONE_ALIGNED_MEMORY(512)
DECLARE_ONE_ALIGNED_MEMORY(1024)
DECLARE_ONE_ALIGNED_MEMORY(2048)
DECLARE_ONE_ALIGNED_MEMORY(4096)

#else

template <size_t Size, size_t Align>
struct AlignedMemory {
    char mem[Size];
} __attribute__((aligned(Align)));

#endif

template <class T>
struct AlignedMemoryFor : public AlignedMemory<sizeof(T), __alignof(T)> {};
Sedimentation answered 28/7, 2012 at 21:50 Comment(0)
G
0

The char array may not be aligned correctly for the size of myclass. On some architectures, that means slower accesses, and on others, it means a crash. Instead of char, you should use a type whose alignment is equal to or greater than that of the struct, which is given by the largest alignment requirement of any of its members.

#include <stdint.h>

class my_class { int x; };

int main() {
    uint32_t storage[size];
    new(storage) my_class();
}

To allocate enough memory for one my_class instance, I think size ought to be sizeof(my_class) / sizeof(T), where T is whichever type you use to get the correct alignment.

Granth answered 3/1, 2011 at 9:7 Comment(2)
"...whose alignment is equal to or greater than that of the struct, which is given by the largest alignment requirement of any of its members" I don't think this is guaranteed, alignment is totally implementation-defined. (That is, it's alignment isn't guaranteed to necessarily be the alignment of strictest member.) Also, int could be greater than 32-bits.Lissa
@GMan: You're right. It works in most cases, though. Here, have a link: #364983Granth

© 2022 - 2024 — McMap. All rights reserved.