0

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].
Filipp
  • 1,843
  • 12
  • 26
  • Presumably, you don't get the warning when accessing `c1 [i - 1]` and `i` happens to be zero, so, happy days, – Paul Sanders Nov 15 '18 at 00:24
  • I do if it happens during evaluation of a `constexpr` function. Anyway, this question is about the "correct" way to write this, rather than just what works. – Filipp Nov 15 '18 at 00:26

2 Answers2

0

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.

Beta
  • 96,650
  • 16
  • 149
  • 150
0

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";
}
Bob__
  • 12,361
  • 3
  • 28
  • 42
  • "Also, accessing `b1` which is not the active member of the union (`a` is the initialized one) is undefined behavior." Is this true despite the common initial subsequence rule? – Filipp Nov 16 '18 at 18:46
  • @Filipp I'd say so, but I'm not a language lawyer. See e.g. https://stackoverflow.com/questions/31388208/active-member-of-an-union-uniform-initialization-and-constructors – Bob__ Nov 16 '18 at 18:59
  • @Filipp It's also one of errors emitted if you try to declare `test` as `constexpr`, for what it's worth: https://wandbox.org/permlink/3aaCWLcXnGaKfgt1 – Bob__ Nov 16 '18 at 19:08