## Determining the index of a value in a struct

Started by 11 months ago9 replieslatest reply 11 months ago95 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

[ - ]

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.

[ - ]

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

[ - ]

#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.

[ - ]

@mrfirmware, you beat me to it!

Max

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.

[ - ]

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

[ - ]

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.

Cheers!

[ - ]

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 :-)

[ - ]

Hi Max,

why do you stick to the union construct?

My suggestion (untested):

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

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


[ - ]