Is there a way to "alias" array element to descriptive variable names in C/C++?

I work in another programming language where you can define an array, then define an variable name to elements of the array. They call it aliasing. It avoids having to execute an equal statement each pass through the code.

The alias for array[3] is Supply_Temp
The alias for array[4] is Return_Temp

This allows the program to utilize the descriptive names rather than the array index and you don't have to manage a while bunch of equal statements. And the aliasing does not take any additional memory. I assume they have a way that defines the alias tag to the same memory location as the array location.

Is there a way to do this in C? I have never tried or had to conceive of it, but it would be a big help in working with Modbus and the register array.

Maybe pointers?

int arr[] = {5, 3, 7, 2, 9};

int *Supply_Temp = &arr[3];
int *Return_Temp = &arr[4];

void setup() {

  Serial.begin(9600);
  Serial.println(*Supply_Temp);
  Serial.println(*Return_Temp);

  *Supply_Temp = 17;
  for (int i = 0; i < 5; i++) Serial.println(arr[i]);

}

void loop() {}

I think that would do it. Pointers have always been my week point.

A static definition can be done with a #define, a constant definition, or an enum.

Regards,
Ray L.

Is it possible to get to the bit level? To create a variable (boolean?) that is equivalent to array[x ] bit 0.

I'm pushing my luck aren't I?

union

typedef union
{
  uint16_t      registers[32];
  
  struct
  {
    uint16_t      pad0;
    uint16_t      pad1;
    uint16_t      pad2;
    uint16_t      Supply_Temp;
    uint16_t      Return_Temp;
    union
    {
      uint16_t        register5;
      
      struct
      {
        uint16_t      Alarm:1;
        uint16_t      Run:1;
      };
    };
  };
}
modbus_registers_t;

modbus_registers_t registers;

void setup( void )
{
  registers.Supply_Temp += 13;
  registers.Return_Temp = registers.Supply_Temp * 7;
  if ( registers.Alarm )
  {
    // Scream and shout.
  }
}

void loop( void )
{
}

Isn't that what enums are all about? Also for the bits, I use this all the time for flags

typedef struct
{
    byte    bit_0: 1;
    byte    bit_1: 1;
    byte    bit_2: 1;
    byte    bit_3: 1;
    byte    bit_4: 1;
    byte    bit_5: 1;
    byte    bit_6: 1;
    byte    bit_7: 1;
} bits;

bits flag1;
#define message_waiting     flag1.bit_0
//#define flag1.bit_1
//#define flag1.bit_2
//#define flag1.bit_3
//#define flag1.bit_4
//#define flag1.bit_5
//#define flag1.bit_6
//#define flag1.bit_7

From then on I simply use, in this case, message_waiting as a boolean flag.

@CodingBadly, I need to digest your solution more.

Alarm is registers[5] bit 0? It has been a long time since I have seen the "uint16_t variable:1;" definition.

@DKWatson, I understand the struct, but not sure how to relate (?) it to the element of the array. Would bits *flag1 = &array[99 ] work? (where array[x ] is of type unit16_t)

The bits example is to get to the bit level. The balance is merely to suggest that #define is the pre-compiler directive to deal with all your aliases.

Use
#define alias_name convoluted_variable_name
anywhere you want, as long as you use it before you use it if that makes sense. It weighs nothing, costs nothing. When the pre-compiler assigns memory and an address to convoluted_variable_name, it replaces alias_name with that same reference everywhere it has been used after it was defined.

adwsystems:
Alarm is registers[5] bit 0?

You will have to test. A given compiler can order either way but it will order them consistently. For example, I recall that Microsoft's C++ Intel compiler orders the opposite of AVR-GCC. Alarm with one of the compilers would be bit 15 and bit 0 with the other.

The test is trivial. Set Alarm to 1 then print register5.

According to gcc documentation, when using bitfields there is no direct mapping of any assignment to any addressable bit.

@DKWatson, reference please.

Follow-up from my earlier post...

adwsystems:
Alarm is registers[5] bit 0?

No. AVR-GCC assigns bit fields from high to low so Alarm is bit 15 and Run is bit 14.

I have not been able to figure out a way to check that at compile time.

I work in another programming language where you can define an array, then define an variable name to elements of the array. They call it aliasing. It avoids having to execute an equal statement each pass through the code.

The alias for array[3] is Supply_Temp

Why are the elements in an array in the first place? It sort-of sounds like a poor-man's alternative to structures, and C/C++ has 'real' structures... For a modbus-like thing:

typedef struct {
  uint8_t fan0:1;
  uint8_t coil1:1;
  uint8_t coil2:1;
  uint8_t coil3:1;
  uint8_t coil4:1;
  uint8_t coil5:1;
  uint8_t coil6:1;
  uint8_t coil7:1;
  uint8_t Supply_Temp;
  uint8_t Return_Temp;
  uint8_t Internal_Temp;
} ac_unit_t;
  :
  if (modbusPak->data[0] == AC_UNIT_VAL) {
     ac_unit_t *ac = &modbusPak->data[1];  // point to the ac unit data
     if (ac->Return_Temp > 60  && ac->fan0 == 0) {
        // temp getting high an fan off; do something to turn it on!
       :
    }
  }

Other than that, #defines are probably the way to go:

#define GET_fan1(pak) (pak->data[2]&2))
#define GET_Supply_Temp(pak) (pak->data[4])

westfw:
Why are the elements in an array in the first place? It sort-of sounds like a poor-man's alternative to structures, and C/C++ has 'real' structures...

The modbus communications are a good example. Modbus can read either a single register (supply_temp) or a group of (contiguous) registers (array[x]). Obviously it is more efficient to read four registers by reading a group which means you need an array. But using the array (and the array indices) throughout the program has two drawbacks. First, the variable is never very descriptive and you are always consulting the magic decoder ring to determine what array[3] is. Second, if you change the array indices (eg., insert Outside Temp between indices 2 and 3) then you must change all the indices throughout the whole program.

Pointers are one way to go. I'm liking CodingBadly's method as it has the layout (order of readable variables) in order, except for the bit thing. I will have to make sure that isn't a problem (or define all 16 bits, ugg.)

DKWatson:
The bits example is to get to the bit level. The balance is merely to suggest that #define is the pre-compiler directive to deal with all your aliases.

Use
#define alias_name convoluted_variable_name
anywhere you want, as long as you use it before you use it if that makes sense. It weighs nothing, costs nothing. When the pre-compiler assigns memory and an address to convoluted_variable_name, it replaces alias_name with that same reference everywhere it has been used after it was defined.

That's only the second half of the question. What you have is a great way to break an int up and provide the bits readable variable names. But the variable flag1 needs to represent array[99] (ie., be the same memory location). so I can access the bits of array[99] using readable variable names.

adwsystems:
I work in another programming language where you can define an array, then define an variable name to elements of the array. They call it aliasing. It avoids having to execute an equal statement each pass through the code.

The alias for array[3] is Supply_Temp
The alias for array[4] is Return_Temp

It seems to me this Thread has drifted away from the question. What's wrong with a simple solution such as

int myAnimalCount[4]; // create an array of 4 elements

#define numberOfDogs 0
#define numberOfCats 1
#define numberOfGerbils 2
#define numberOfParrots 3

myAnimalCount[numberOfDogs] = 23;

...R

I say still on target, just describing different ways to slice the bread.

Nothing wrong with your solution. In light of my response to westfw, I see where your line of thinking was. I was scoped locked on the method available at my day job and posed the question in those terms. CodingBadly, Blue Eyes, et al. provided solutions based on the requirements. Add in my response to the question posed by westfw, your option is to just name the indices will also meet the original requirement.

How about the enhanced question of making readable variable names for the bits of one of the array ints?

adwsystems:
Pointers are one way to go. I'm liking CodingBadly's method as it has the layout (order of readable variables) in order, except for the bit thing. I will have to make sure that isn't a problem (or define all 16 bits, ugg.)

or a simple reference as an alias (instead of a pointer and having to use all that dereferencing):

int array[10];

int& value3 = array[3];  // value3 is a reference to the [3] element of array

void setup() {
  Serial.begin(9600);
  for (auto& a :array) {
    a = 10;
  }
  Serial.println(value3);
}

void loop() {

}

BulldogLowell:
or a simple reference as an alias (instead of a pointer and having to use all that dereferencing):

See, I told your pointers/referencing have always been my Achilles heel.