0

Based on a comment and answer on my other question I came out with this code. The comment was according to 5.3.4 New [expr.new] it is allowed to access outside of the struct as long as it is allocated. However I coudln't find the section that says that.

I came up with this code. I wanted to know if its portable and completely defined and legal. The output is kind of interesting. in gcc it is 10,10,12 while visual studios 2010 shows 12,10,12.

Is the code legal in C++11 or C++03? I believe the code will be align in any platform/cpu with a standard compiler.

-edit- In case your lazy flex_struct is the part that does questionable things.

#include <iostream>
#include <new>
#include <cstring>

using namespace std;

template <typename STRUCT, typename TYPE> class flex_struct {
    flex_struct(){}
public:
    //should be safe to access and check what length the array is
    static STRUCT* head(char*buff) {
        //is this next line wrong?
        //if((alignof(STRUCT)%reinterpret_cast<size_t>(buff))!=0) { throw std::exception(); }
        return reinterpret_cast<STRUCT*>(buff);
    }

    struct struct_with_array : public STRUCT { TYPE buf[1]; };

    TYPE* buff() {
        //if(length==0) { throw std::exception(); }
        auto p = reinterpret_cast<struct_with_array*>(this);
        return p->buf;
    }
};

typedef short testtype;

struct MyVariableLengthStruct : public flex_struct<MyVariableLengthStruct, testtype> {
    int a, b;
    char c;
};

struct MyVariableLengthStruct2 {
    int a, b;
    char c;
    testtype buf[1];
};
struct MyVariableLengthStruct3a {
    int a, b;
    char c;
};
struct MyVariableLengthStruct3 : MyVariableLengthStruct3a {
    testtype buf[1];
};
int main() {

    auto srcarray=new char[1024];
    //we don't actually need this line which is incorrect anyways (sizeof isnt 1024)
    //memset(srcarray, 0, sizeof(srcarray)); //whats a C++ way to do this w/o writing a loop or function?
    auto v = MyVariableLengthStruct::head(srcarray);
    auto buff = v->buff();
    auto dif1 = (int)buff-(int)v;
    printf("%X %X %d\n", v, buff, dif1);
    MyVariableLengthStruct2 v2;
    auto dif2 = (int)v2.buf-(int)&v2;
    printf("%X %X %d\n", &v2, v2.buf, dif2);
    MyVariableLengthStruct3 v3;
    auto dif3 = (int)v3.buf-(int)&v3;
    printf("%X %X %d\n", &v3, v3.buf, dif3);
}
Community
  • 1
  • 1

1 Answers1

4

I see a lot of issues with this code.

    //is this next line wrong?
    //if((alignof(STRUCT)%reinterpret_cast<size_t>(buff))!=0) { throw std::exception(); }
    return reinterpret_cast<STRUCT*>(buff);

Memory returned from new char[n] is properly aligned for objects of the requested size or less. However, static and automatic char arrays are not. So, if you always pass something allocated with new char[n] here, there's no problem. If you pass a pointer to a static or automatic array, then alignment is a problem indeed.

struct struct_with_array : public STRUCT { TYPE buf[1]; };

Indexing out of array bounds is undefined behaviour. There is only one element of buf you can access. Note that this issue doesn't happen in the code in the original answer.

auto p = reinterpret_cast<struct_with_array*>(this);

I'm not entirely sure of the validity of this cast, but I suspect it breaks strict aliasing.

auto srcarray=new char[1024];
memset(srcarray, 0, sizeof(srcarray)); //whats a C++ way to do this w/o writing a loop or function?

The C++ way would be std::vector<char>(1024);, I guess. In any case sizeof(srcarray) is likely not 1024, but probably 4 or 8 (equal to sizeof(char*)).

auto v = MyVariableLengthStruct::head(srcarray);

There was never a MyVariableLengthStruct constructed in the memory pointed by srcarray, so this will only work for POD types.

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
  • 1) Indexing out of array bounds <-- That never happens assuming you check if the size of the array is >0 and don't call buff() blindly. 2) I vaguely remember aliasing rules. But IIRC as long as the buffer isn't read outside of the function before the function finish executing i should be safe. 3) Great, because I only want to do this with POD types. I need some kind of static_assert to make sure STRUCT is POD. –  Mar 06 '12 at 14:05
  • @acidzombie The size of the array is 1: `TYPE buf[1];`. The assert you want for PODness is `static_assert(std::is_pod::value, "STRUCT should be POD");`. – R. Martinho Fernandes Mar 06 '12 at 14:08
  • Yep but i was actually trying to figure out why my struct failed. I'm testing MyVariableLengthStruct3. I'll figure it out and comment later –  Mar 06 '12 at 14:19
  • No, arrays don't make it non-POD. Arrays of standard layout types have standard layout. See [here](http://stackoverflow.com/a/7189821/46642). I'm a bit confused about the output code, though. `v2.buf - &v2`? Shouldn't that be negative? How can it be 10? – R. Martinho Fernandes Mar 06 '12 at 14:26
  • About POD http://stackoverflow.com/questions/9585720/why-isnt-this-a-pod-type-c11 well the address of v2.anymember is always >= v2. Even tho its on the stack structs are still X size and members are still in same layout as if it was on the heap. The first member is at offset 0, next member located at a > offset. –  Mar 06 '12 at 14:38