I have read that offsetof macro is commonly implemented as:
#define offsetof(st, m) \
((size_t)&(((st *)0)->m))
And according to Wikipedia there is debate about whether this is undefined behavior because it may be dereferencing a pointer. I have a buffer of information that I need to send to different places, but the buffer only takes up part of the struct. I was wondering if the following is guaranteed to give the correct size of my struct up to the end of the buffer, the last member.
struct PerObjBuffer
{
mat4 matrix;
mat4 otherMatrix;
vec4 colour;
int sampleType = 0;
size_t getBufferSize() { return offsetof(PerObjBuffer, sampleType) + sizeof(sampleType); }
void sendToGPU() { memcpy(destination, this, getBufferSize()); }
String shaderStringCode =
R"(layout(binding = 2, std140) uniform perObjectBuffer
{
mat4 WVPMatrix;
mat4 worldMatrix;
vec4 perObjColour;
int texSampleFormat;
};
)";
}
If the use of offsetof isn't a good idea, what's the best way to do what I want to do?
Edit: What about sizeof(PerObjBuffer) - sizeof(String)? Would I have to take into account padding issues?
The standard specifies behaviors of things, not how are they implemented. The implementation part is left to implementators - people who write compilers and libraries - and they have to implement things in a way they work in a way specified by the standard. If that implementation of standard library has by itself undefined behavior is irrelevant - it has to work with specified compiler, so specific compiler will interpret it in a way it has the behavior implementators want. Undefined behavior means that the standard specifies no requirement on the behavior of code. Your compiler documentation may specify additional requirements specifying behaviors of code that according to the standard are undefined - thus the code may be undefined by the standard and be perfectly fine and reasonable on a specific compiler that is required/written to interpret it in that way.
C++ language uses macros from C language when proper
#include
is used. The C standard says C99 7.17p3:As a user of the language you do not care how it's implemented. If you use a standard compliant compiler, whatever it does behind the scenes, it should result in the behavior specified by the standard. Your usage of
offsetof(PerObjBuffer, sampleType)
is valid - havingstatic PerObjBuffer t;
then&(t.member-sampleType)
evaluates to address constant - sooffsetof(PerObjBuffer, sampleType)
evaluates to integer constant expression. You like do not care how the compiler arrives at the result - it can use dark magic to do it - what matters it that compiler does it and the result represents the offset in bytes. (I think the famous example ismemcpy
- it's not possible to implementmemcpy
in standard compliant way, yet the function... exists).Still, I fear that other parts of your code will be very invalid. The
memcpy
will most probably result in undefined behavior - you will copy padding between members and you seem to want to send that to some hardware location. In any case, I advise to do extensive research on lifetime and representation of C++ objects, on padding within structures, types of objects (ie.POD
/standard layout/trivial) and how to work with them (ie. when it's ok to usemem*
functions/placement new/std::launder
).