I have two structures in c++
struct Vertex {
float x,y,z;
float nx, ny, nz;
};
struct SkinnedVertex : public Vertex {
uint32_t j0, j1, j2, j3;
float w0, w1, w2, w3;
}
Now, when I use offsetof to get offset of Vertex, everything is fine but if I use offsetof in SkinnedVertex, I get the following warning when compiling with GCC:
warning: offsetof within non-standard-layout type ‘SkinnedVertex’ is conditionally-supported
My guess is that this happens because I am inheriting SkinnedVertex from Vertex, which makes this a non-standard-layout. But why does this happen? Does inheritance add additional information that makes this a non standard layout? Is thee a way to enforce it so that both of these structures are in standard layout?
EDIT: Changed jN types to uint32_t (was float)
https://en.cppreference.com/w/cpp/named_req/StandardLayoutType
A standard layout type requires all data members be in the same type.
Standard layout is conservative in definition. Additional types could be made standard layout. The fact that a type is not standard layout doesn't mean there is a good reason that it isn't. It just means the standard committee and compilers haven't done the work to justify it. I mean, you could make classes with virtual functions standard layout if the committee really wanted (like, standardize how vtables work).
One concern is that the committee doesn't want to needlessly break existing code. So they try to standardize existing practice unless there is a good reason. If two compilers disagree on layout of a class, that makes standardizing their layout more costly and/or generate less benefits.
Standard layout is technically just a flag; semantically it means different compilers with similar options (like packing) are going to compile compatible data layouts.
Related
For reference, the interference size is part of C++17, P0154R1, and mandated declaration order is proposed for C++23, P1847R4.
As far as I understand...
The first proposal requires the compiler to move alignas-ed member variables closer together / farther away.
The second proposal would require the compiler to lay out member variables in order of declaration in the class.
Seems to me the 2nd proposal takes the punch out of the first. hardware_destructive_interference_size would necessarily require leaving unused memory between the two member variables, with no option to fill it with other members. hardware_constructive_interference_size would be reduced to a warning saying "can't do it, try reordering the member variables yourself".
P0154 makes no changes to how member variables are laid out. It merely exposes some constexpr variables, which you can use to adjust the alignment of member variables through alignas. But alignas doesn't gain any special properties.
That is, these two structs have the same layout, if hardware_destructive_interference_size is 64:
struct one
{
alignas(hardware_destructive_interference_size) int member1;
alignas(hardware_destructive_interference_size) int member2;
};
struct two
{
alignas(64) int member1;
alignas(64) int member2;
};
P1847 doesn't actually change any behavior. It removes a degree of freedom that was previously available to compilers when laying out the order of members in a class. This removal doesn't change any compiler's behavior because... no compilers took advantage of this degree of freedom. That's why the committee is removing it; nobody did anything with it.
Also, said degree of freedom is the ability to layout members with different access classes (public/private). The layout of members within an access class has been declaration order since (at least) C++11.
So these two changes have nothing to do with one another.
I've just learned from Bug in VC++ 14.0 (2015) compiler? that one shouldn't make assumptions about how a struct's layout will end up in memory. However, I don't understand how it is common practice in a lot of code I've seen. For example, the Vulkan graphics API does the following:
Defines a struct
struct {
glm::mat4 projection;
glm::mat4 model;
glm::vec4 lightPos;
} uboVS;
Then fills up its fields:
uboVS.model = ...
uboVS....
Then just copies over the struct (in host memory) to device memory via memcpy:
uint8_t *pData;
vkMapMemory(device, memory, 0, sizeof(uboVS), 0, (void **)&pData);
memcpy(pData, &uboVS, sizeof(uboVS));
vkUnmapMemory(device, memory);
Then over to the GPU, it defines a UBO to match that struct:
layout (binding = 0) uniform UBO
{
mat4 projection;
mat4 model;
vec4 lightPos;
} ubo;
Then, on the GPU side, ubo will always match uboVS.
Is this the same undefined behavior? Doesn't that code rely on the uboVS struct to be laid out exactly as defined, or for both sides (the compiled C++ code and the compiled SPIR-V shader) to basically generate the same different struct layout? (similar to the first example in https://www.securecoding.cert.org/confluence/display/c/EXP11-C.+Do+not+make+assumptions+regarding+the+layout+of+structures+with+bit-fields)
This question is not specific to Vulkan or graphics APIs, I am curious as to what exactly can one assume and when is it ok to just use a struct as a chunk of memory. I understand struct packing and alignment, but is there more to it?
Thanks
It's important to recognize the difference between what you did in the question you cite and what you're doing here.
What you did in the question you showed breaks the rules of C++. It invokes undefined behavior. You tried to pretend that an object containing 16 floats is the same thing as a 16-float array. C++ doesn't permit this to be well-defined behavior, and compilers are allowed to assume you won't try it.
By contrast, converting a struct into a byte array and copying that array somewhere else actually doesn't break the rules of the C++ object model. It has a very specific clause permitting such things for appropriate types.
The difference is that it's not the C++ compiler that cares about the object's layout; it's the GPU. So long as the layout of data you provide matches what your shader said it would be, you're fine. You're not casting floats into arrays or trying to access one object through a pointer to a different one or somesuch. You're just copying bytes.
At which point, the only question that remains is whether the byte representation of that struct matches the byte representation of the expected SPIR-V data structure definition. And yes, this is something you can rely upon for most systems that Vulkan can run on.
It is true that, roughly speaking, the C++ standard does not mandate any particular internal layout of class members.
However, specialty libraries, like graphics libraries for a particular operating system, are going to be targeting the operating system's specific compiler. They know how this particular compiler arranges the layout of C/C++ class and structure members, and the library is going to supply suitable definitions that matches the actual hardware in question.
Operating systems that have more than one compiler will often have a formal specification for that operating system's binary ABI, and the compilers are going to follow that ABI, and the specialty library will provide class and structure definitions that will be in sync with that.
So, in your specific case, you can "assume and when is it ok to just use a struct as a chunk of memory" after you consult your compiler's documentation, determine how your compiler lays out the members of structures or classes, and then come up with a structure layout accordingly.
Spir-V (the shading language you pass to vulkan) requires that you add layout decorations to struct members used for UniformConstant, Uniform, and PushConstant variables. You can use this to make the spir-V member offsets match the member offsets in the C++ struct.
To actually do this is tricky as it requires inspecting the spir-V code and setting the offsets as needed.
I think that unions can be perfect for what i have in mind and especially when I consider that my code should run on a really heterogeneous family of machines, especially low-powered machine, what is bugging me is the fact that the people who creates compilers doesn't seem to care too much about introducing and offering a good union support, for example this table is practical empty when it comes to Unrestricted Unions support, and this is a real unpleasant view for my projects.
There are alternatives to union that can at least mimic the same properties ?
Union is well supported on most compilers, what's not well supported are unions that contains members that have non trivial constructor (unrestricted unions). In practice, you'd almost always want a custom constructor when creating unions, so not having unrestricted union is more of a matter of inconvenience.
Alternatively, you can always use void pointer pointing to a malloc-ed memory with sufficient size for the largest member. The drawback is you'd need explicit type casting.
One popular alternative to union is Boost.Variant.
The types you use in it have to be copy-constructible.
Update:
C++17 introduced std::variant. Its requirements are based on Boost.Variant. It is modernized to take into account the features in C++17. It does not carry the overhead of being compatible with C++98 compilers (like Boost). It comes for free with standard library. If available it is better to use it as alternative to unions.
You can always do essentially the same thing using explicit casts:
struct YourUnion {
char x[the size of your largest object];
A &field1() { return *(A*)&x[0]; }
int &field2() { return *(int*)&x[0]; }
};
YourUnion y;
new(&y.field1()) A(); // construct A
y.field1().~A(); // destruct A
y.field2() = 1;
// ...
(This is zero overhead compared to, e.g., Boost.Variant.)
EDIT: more union-like and without templates.
Is the way C++ structs are laid out set by the standard, or at least common across compilers?
I have a struct where one of its members needs to be aligned on 16 byte boundaries, and this would be easier if I can guarantee the ordering of the fields.
Also, for non-virtual classes, is the address of the first element also likely to be the address of the struct?
I'm most interested in GCC and MSVS.
C and C++ both guarantee that fields will be laid out in memory in the same order as you define them. For C++ that's only guaranteed for a POD type1 (anything that would be legitimate as a C struct [Edit: C89/90 -- not, for example, a C99 VLA] will also qualify as a POD).
The compiler is free to insert padding between members and/or at the end of the struct. Most compilers give you some way to control that (e.g., #pragma pack(N)), but it does vary between compilers.
1Well, there is one corner case they didn't think of, where it isn't guaranteed for a POD type -- an access specifier breaks the ordering guarantee:
struct x {
int x;
int y;
public:
int z;
};
This is a POD type, but the public: between y and z means they could theoretically be re-ordered. I'm pretty sure this is purely theoretical though -- I don't know of any compiler that does reorder the members in this situation (and unless memory fails me even worse than usual today, this is fixed in C++0x).
Edit: the relevant parts of the standard (at least most of them) are §9/4:
A POD-struct is an aggregate class that has no non-volatile
data members of type pointer to member, non-POD-struct, non-
POD-union (or array of such types) or reference, and has no
user-defined copy assignment operator and no user-defined
destructor.
and §8.5.1/1:
An aggregate is an array or a class (clause 9) with no user-
declared constructors (12.1), no private or protected non-
static data members (clause 11), no base classes (clause 10)
and no virtual functions (10.3).
and §9.2/12:
...the order of allocation of non-static data members
separated by an access-specifier is unspecified (11.1).
Though that's restricted somewhat by §9.2/17:
A pointer to a POD-struct object, suitably converted using
a reinterpret_cast, points to its initial member...
Therefore, (even if preceded by a public:, the first member you define must come first in memory. Other members separated by public: specifiers could theoretically be rearranged.
I should also point out that there's some room for argument about this. In particular, there's also a rule in §9.2/14:
Two POD-struct (clause 9) types are layout-compatible if they have the same number of nonstatic data members, and corresponding nonstatic data members (in order) have layout-compatible types (3.9).
Therefore, if you have something like:
struct A {
int x;
public:
int y;
public:
int z;
};
It is required to be layout compatible with:
struct B {
int x;
int y;
int z;
};
I'm pretty sure this is/was intended to mean that the members of the two structs must be laid out the same way in memory. Since the second one clearly can't have its members rearranged, the first one shouldn't be either. Unfortunately, the standard never really defines what "layout compatible" means, rendering the argument rather weak at best.
C++ inherits from c the need/desire to work efficiently on many platforms, and therefore leaves certain things up to the compiler. Aside from requiring elements to appear in the specified order, this is one of them.
But many compilers support #pragmas and options to let you establish come control of the packing. See your compiler's documentation.
Is the way C++ structs are laid out
set by the standard, or at least
common across compilers?
There is no guarantee in the standard. I would not depend on compilers having the same alignment
I have a struct where one of its
members needs to be aligned on 16 byte
boundaries, and this would be easier
if I can guarantee the ordering of the
fields.
Compilers will not change the order of the fields.
Here are some links on how to set them for both GCC and MSVC:
For GCC: http://developer.apple.com/mac/library/documentation/DeveloperTools/gcc-4.0.1/gcc/Structure_002dPacking-Pragmas.html
MSVC: http://msdn.microsoft.com/en-us/library/ms253935(VS.80).aspx and http://msdn.microsoft.com/en-us/library/2e70t5y1(VS.80).aspx
I would keep them as structures and use an extern "C" to ensure that it works properly. Maybe not needed but that would definitely work.
C structs (and C++ PODs, for compatibility) are required by the standard to have sequential layout.
The only difference between compilers is alignment, but fortunately, both MSVC and GCC support #pragma pack.
If you want to see the memory layout of all class types in memory under MSVC, add the /d1reportAllClassLayout switch to the compiler command line.
If you want to see what one class is layed out like, add /d1reportSingleClassLayoutNNNNN where NNNNN is the name of the class.
Structures are laid out sequentially in memory. However, the way they're aligned in memory varies based on the operating system.
For things bigger than 4 bytes, there's a difference between Windows and Linux. Linux aligns them as if they're 4 bytes, so for example a double (8 bytes) could start at p+4, p+8, p+12, etc. where p is the start of the structure. In Windows, a double (8 bytes) needs to start at an address that's a multiple of 8 so p+8, p+16, p+24, etc.
This is a question that was sparked by Rob Walker's answer here.
Suppose I declare a class/struct like so:
struct
{
char A;
int B;
char C;
int D;
};
Is it safe to assume that these members will be declared in exactly that order in memory, or is this a compiler dependent thing? I'm asking because I had always assumed that the compiler can do whatever it wants with them.
This leads into my next question. If the above example causes memory alignment issues, why can the compiler not just turn that into something like this implicitly:
struct
{
char A;
char C;
int B;
int D;
};
(I'm primarily asking about C++, but I'd be interested to hear the C answer as well)
Related topics
Why doesn't GCC optimize structs?
C99 §6.7.2.1 clause 13 states:
Within a structure object, the
non-bit-field members and the units in
which bit-fields reside have addresses
that increase in the order in which
they are declared.
and goes on to say a bit more about padding and addresses. The C89 equivalent section is §6.5.2.1.
C++ is a bit more complicated. In the 1998 and 2003 standards, there is §9.2 clause 12 (clause 15 in C++11):
Nonstatic data members of a
(non-union) class declared without an
intervening access-specifier are
allocated so that later members have
higher addresses within a class
object. The order of allocation of
nonstatic data members separated by an
access-specifier is unspecified
(11.1). Implementation alignment
requirements might cause two adjacent
members not to be allocated
immediately after each other; so might
requirements for space for managing
virtual functions (10.3) and virtual
base classes (10.1).
The data members are arranged in the order declared. The compiler is free to intersperse padding to arrange the memory alignment it likes (and you'll find that many compilers have a boatload a alignment specification options---useful if mixing bits compiled by different programs.).
See also Why doesn't GCC optimize structs?.
It appears that this answer is somewhat obsolete for C++. You learn something everyday. Thanks aib, Nemanja.
I cannot speak for C++, but in C the order is guaranteed to be the same order in memory as declared in the struct.
Basically, you can count on that only for the classes with a standard layout. Strictly speaking, standard layout is a C++0x thing, but it is really just standardizing existing practice/
Aside from padding for alignment, no structure optimization is allowed by any compiler (that I am aware of) for C or C++. I can't speak for C++ classes, as they may be another beast entirely.
Consider your program is interfacing with system/library code on Windows but you want to use GCC. You would have to verify that GCC used an identical layout-optimization algorithm so all your structures would be packed correctly before sending them to the MS-compiled code.
While browsing the related topics at the right, I looked at this question. I figure this may be an interesting corner case when thinking about these issues (unless it's more common than I realize).
To paraphrase, if you have a struct in C that looks something like this:
struct foo{};
and subclass it like so in C++ (using a separate compilation unit):
extern "C" foo;
struct bar: public foo{};
Then the memory alignment won't necessarily be the same for the reasons aib mentions (even amongst compilers from the same vendor).