Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Debugger-friendly zero-cost named references to specific locations in an array

Tags:

c++

c++14

struct A
{
    int a   = 1;
    short b = 2;
    char c  = 3;
}

struct B
{
    using arr_type = array<A,3>;
    char asd = 0;
    A a1;  
    A a2;
    A a3;

    // is this safe to use to loop trough all 3 elements?
    arr_type* p1 = reinterpret_cast<arr_type*>(&a1);

    // or maybe this one?
    A* p2 = &a1;
};

Can I safely use p1 or p2 to loop from a1...a3 ?

B b;

for (int i = 0; i < 3; i++)
{
     cout << p1[i];
     cout << p2[i];
}

The reason why it's not a simple array is because I want each "item" to have a proper name.

I could instead use the union approach, but the C++ prohibits anonymous structs (altho this is not a problem for me since MSVC supports this and GCC seems to support it as well);

union E
{
    A arr[3];
    struct {
        A a1;
        A a2;
        A a3;
    };
};

And the following is clearly safe, but it has a 4 byte overhead for each reference. Which I don't like. (Plus the cost to initialize the references..)

struct B
{
    char asd;
    A arr[3];  
    A& a1 = arr[0];
    A& a2 = arr[1];
    A& a3 = arr[2];
};

And this one has no overhead but for my very specific case, it's not good enough.

struct B
{
    char asd;
    A arr[3];  
    A& a1() { return arr[0] };
    A& a2() { return arr[1] };
    A& a3() { return arr[2] };
};

I'm gonna be using those a1, a2, a3 names very often, and it's harder to debug them if they are function calls in visual studio. And again, I'm going to be using those fields a lot, so I want to be able to check their values easily.

like image 213
Gam Avatar asked Apr 07 '16 04:04

Gam


1 Answers

struct B
{
     using arr_type = array<A,3>;
     char asd = 0;
     A a1;  
     A a2;
     A a3;

     // is this safe to use to loop trough all 3 elements?
     arr_type* p1 = reinterpret_cast<arr_type*>(&a1);
 };

Structs need to align naturally for their types, and so do arrays, but I don't know of any rule saying they have to be the same alignment points.

If there were such a rule that struct layout boundaries for members like this and array boundaries will be the same--it would only apply to standard layout structs:

https://stackoverflow.com/a/7189821/211160

All bets would be off if you did something like:

private:
    A a1;
    A a2;
public:
    A a3;

I'd imagine all bets would be off if it contained anything that flipped off the standard layout switch. As it was questionable to start with, I'd say don't even do it then.

(I'd also wonder what kind of differences #pragma pack() would throw in for arrays vs. structs...not that #pragmas are in the standard, I just wonder.)

union E
{
    A arr[3];
    struct {
        A a1;
        A a2;
        A a3;
    };
};

No, arr[N] and aN would not be equivalent. There are some subtle details about how you can use initial sequences in unions for compatible reading in C++...but that's only between structures with compatible sequences. It says nothing about a struct and an array:

Type punning a struct in C and C++ via a union

I'm gonna be using those a1, a2, a3 names very often, and it's harder to debug them if they are function calls in visual studio. And again, I'm going to be using those fields a lot, so I want to be able to check their values easily.

"And the following is clearly safe, but it has a 4 byte overhead for each reference"

In practice it appears you are correct, that today's GCC isn't optimizing it out (per your links):

https://godbolt.org/g/6jAtD5

http://ideone.com/zZqfor

That's disappointing and they could be optimized out, as there's nothing in the standard saying they have to take up space. They point internally to the structure, and they don't change for the lifetime of the structure. :-/

Your complaint against the function access which would be optimized away was that it wasn't debugger-friendly enough. Why not do both?

struct B
{
    char asd;
    A arr[3];

    A& a1() { return arr[0] }
    const A& a1() const { return arr[0]; }
    A& a2() { return arr[1] };
    const A& a2() const { return arr[1]; }
    A& a3() { return arr[2] };
    const A& a3() const { return arr[2]; }

#if !defined(NDEBUG)
    A& a1_debug = arr[0];
    A& a2_debug = arr[1];
    A& a3_debug = arr[2];
#endif
};

If debugger-friendliness features of projecting your data structures is important to you...it might be a good use of time to learn how to write custom debugger helpers for your environment, e.g.:

http://doc.qt.io/qtcreator/creator-debugging-helpers.html

I guess whether that's worth it depends on how often you have this kind of concern.

like image 70
HostileFork says dont trust SE Avatar answered Oct 22 '22 19:10

HostileFork says dont trust SE