Determining the index of a value in a struct

Started by MaxMaxfield 3 years ago9 replieslatest reply 3 years ago146 views

This is based on some questions I posed (and you all answered) before. Suppose I have declared NUM_SETTINGS to be equal to 3. Let's also assume I've defined FRED to be 6. Now suppose I declare a union that looks like the following:

union Settings {
    struct {
        uint8_t DateFormat;
        uint8_t TimeFormat;
        uint8_t Location;   
    } str;

    uint8_t arr[NUM_SETTINGS];

} WrkSettings;

OK, suppose I assign a value to the TimeFormat as follows:

WrkSettings.str.TimeFormat = FRED;

The TimeFormat field in the struct portion of the union corresponds to element [1] in the case of the "arr" (array) representation of the union.

Suppose I also want to update this item in the EEPROM -- if I know it's element 1, I could use:

EEPROM.write(1, FRED);

But what i really want to do is have some way to say:

iSet = <get index value of>WrkSettings.srt.TimeFormat;

EEPROM.write(iSet, FRED);

Is there any way to do the "<get the index of>" part?

Thanks as always -- Max

[ - ]
Reply by matthewbarrAugust 6, 2021

Hi Max, my understanding is that the compiler handles memory layout behind the scenes for the two representations in the union, and there is no language defined, compiler independent way that this is required to be done such that what you want to do will always work the same way.

So, I believe the answer to your last question is that you may be able to create a 'get index of' function that you can rely on for a particular architecture and compiler run a particular way, but in general it isn't going to be portable.

[ - ]
Reply by MaxMaxfieldAugust 6, 2021

Hi Matthew -- thanks for the "heads-up" -- Max

[ - ]
Reply by mrfirmwareAugust 6, 2021

I don't really know what you're asking for but maybe offsetof() can help you?

#include <stddef.h>


size_t const index = offsetof(union Setings, str.TimeFormat); //  ??? Not sure this will work

// Do you just wan to write the value at WorkSetting.str.TimeFormat? 
EEPROM.write(index, &Worksetting.str.TimeFormat);

Friendly suggestion, be consistent with your naming. In your scheme 'arr' should 'Arr'. Similarly 'str' --> 'Str'. Either snake_case, TitleCase, or camelCase and then UPP_CASE for enums and #defines.

[ - ]
Reply by waydanAugust 6, 2021

@mrfirmware, you beat me to it!


There is helpful information on offsetof here https://en.cppreference.com/w/c/types/offsetof. That page also includes a web-based compiler with editable test code, and it seems to work with unions.

one word of caution is that if you’re actually using a C++ compiler, offsetof is undefined for any type without “standard layout”. If you stick to C-style type definitions (e.g. no inheritance) you shouldn’t have an issue.

[ - ]
Reply by MaxMaxfieldAugust 6, 2021

Hi Mr Firmware -- I totally agree regarding the naming -- personally I use Upper Camel Case for global variables and Lower Camel Case of local variables -- my example was culled from some code from a friend (that's my story and I'm sticking to it LOL)

FYI Your example works just fine -- thanks so much -- Max

[ - ]
Reply by DilbertoAugust 6, 2021

Hi Max!

Just completing the answer from @mrfirmware, it's worth to say that C's offsetof() macro is an ANSI C library feature found in stddef.h, that is, the offset reported is always in bytes.

For the union you have created it must work fine, but for more complex data structures maybe it's not worth it.


[ - ]
Reply by MaxMaxfieldAugust 6, 2021

As you say, since we are using only byte-size fields in out union, this does work well. I'm just happy to have learned a new trick :-)

[ - ]
Reply by tcfkatAugust 6, 2021

Hi Max,

why do you stick to the union construct?

My suggestion (untested):

#define EEROMOFF 0
enum SettingsOffset { DateFormat = 0, TimeFormat, Location };
uint8_t WrkSettings [SETTINGSSIZE];

WrkSettings [TimeFormat] = FRED;
EEPROM.write (TimeFormat + EEROMOFF, FRED);

[ - ]
Reply by MaxMaxfieldAugust 6, 2021

I guess because I started off wandering down a certain path and just kept on going LOL