Using negative array index to access preceeding member

128 Views Asked by At

I'd like to use a negative array index to access the same-type member that immediately precedes that array in a struct.

Consider this type of code:

union hello {
    int a[2];
    struct { int b0; int b1[1]; };
};

I want to use b1[-1] to access b0.

When I try to do this, clang and gcc seem to understand exactly what I want.

extern const int test = hello{{42, 1337}}.b1[-1];

This correctly determines at compile time that test is really 42.

Unfortunately, clang produces a warning that -1 is not in bound. Gcc does too if I change const to constexpr.

What is the correct way to write this type of code?

Here are the ways I already know but don't like:

  • Use a[] with 1-based indexing.
  • Make b1 a pointer that points to a[1].
2

There are 2 best solutions below

3
On

When I try to do this, clang and gcc seem to understand exactly what I want

Yes, but they also generate some diagnostic, if asked (gcc):

prog.cc:6:33: warning: ISO C++ prohibits anonymous structs [-Wpedantic]
     struct { int b0; int b1[1]; };

Also, accessing b1 which is not the active member of the union (a is the initialized one) is undefined behavior.

You could instead write a class which encapsulates the data and the wanted access logic:

#include <iostream>
#include <array>

template<size_t Dim>
class Hello
{
    std::array<int, Dim> data_;
public:
    template<class... ArgType>
    constexpr Hello(ArgType... args) : data_{args...} {};

    constexpr int first()       const noexcept { return data_[0]; }

    constexpr int one_based(int i) const { return data_.at(i + 1); }
    constexpr int zero_based(int i) const { return data_.at(i); }
};

int main()
{
    constexpr Hello<2> hi {42, 1337};

    static_assert(hi.first() == 42);

    static_assert(hi.one_based(-1) == 42);
    static_assert(hi.one_based(0) == 1337);

    static_assert(hi.zero_based(0) == 42);
    static_assert(hi.zero_based(1) == 1337);

    std::cout << "So far, so good...\n";
}
0
On

If I understand your question correctly, you have variables {c0, c1, c2, c3,...}, and sometimes you want to treat them as an array [c1, c2, c3,...], an at other times as an array [c0, c1, c2, c3,...].

(I'm not sure I understand why you want to do this, but never mind.)

Here's one solution:

int A[5];
int *B = A+1;
A[0] = c0;
A[1] = c1;
A[2] = c2;
....

Now you can iterate over A[i] if you want to include c0, and over B[i] if you don't.