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].
2
  • Presumably, you don't get the warning when accessing c1 [i - 1] and i happens to be zero, so, happy days, Commented Nov 15, 2018 at 0: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. Commented Nov 15, 2018 at 0:26

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

Comments

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";
}

3 Comments

"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 I'd say so, but I'm not a language lawyer. See e.g. stackoverflow.com/questions/31388208/…
@Filipp It's also one of errors emitted if you try to declare test as constexpr, for what it's worth: wandbox.org/permlink/3aaCWLcXnGaKfgt1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.