How to update data to members of a structure

I have a large structure defined that I need to regulary update the values to the members.
Now I have in my update function:

sMyStruct.Member1 = Update("Member1");
sMyStruct.Member2 = Update("Member2");
.
.
.
sMyStruct.Member99 = Update("Member99");

How can I use the pointer to the structure and update all the structure members in a more code efficient way?
First of all, if you have a pointer to a struct, the correct syntax is:
1
2
3
4
void update_struct(my_struct *ptr_to_struct)
{
    ptr_to_struct->member = new_value; // note the "->" operator to access members!
}


Secondly, you should consider using an array in your struct, instead of a zillion of separate members!
1
2
3
4
5
6
typedef struct
{
    int data[42];
    /* ... possible more members here ... */
}
my_struct;


...it allows you to update the struct like this:
1
2
3
4
5
6
7
void update_struct(my_struct *ptr_to_struct)
{
    for (size_t i = 0; i < 42; ++i)
    {
        ptr_to_struct->data[i] = update(i);
    }
}


Of course, a struct containing only an array would be pointless. Just use a "bare" array!
Last edited on
How can I use the pointer

What pointer? I see no pointer in what you've posted.

I certainly have to question why you have 99 separate member entries.
Why not use an array and a for loop?
1
2
3
4
5
6
7
8
9
10
11
12
//    Requires C++20
int main()
{
	string arg;
	//	Assuming sMyStruct.Member is declared [100]
	//	Entry 0 is ignored
	for (int i = 1; i < 100; i++)
	{
		arg = format("Member{}", i);
		sMyStruct.Member[i] = Update(arg);
	}
}

If you don't have C++20, you can use a C string and sprintf.
Last edited on
Note that the array solutions suggested above only works for members of the same type.
If different types are needed, you could have one array per type:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef struct
{
    int data_1[42];
    double data_2[42];
}
my_struct;

void update_struct(my_struct *ptr_to_struct)
{
    for (size_t i = 0; i < 42; ++i)
    {
        ptr_to_struct->data_1[i] = update_1(i);
        ptr_to_struct->data_2[i] = update_2(i);
    }
}


...or even an array of struct/union:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
typedef struct
{
    int value_1;
    double value_2;
}
element_t;

typedef struct
{
    element_t data[42];
    /* ... possible more members here ... */
}
my_struct;

void update_struct(my_struct *ptr_to_struct)
{
    for (size_t i = 0; i < 42; ++i)
    {
        ptr_to_struct->data[i].value_1 = update_1(i);
        ptr_to_struct->data[i].value_2 = update_2(i);
    }
}
Last edited on
If they are of different types, then possibly a std::variant then use std::visit() etc

https://en.cppreference.com/w/cpp/utility/variant/visit
I probably did not explained the structure good enough, so I try again.

How can I improve the coding of the ReadLVars function so I avoid coding 200+ lines?

The members must be in a structure as the structure is used in other parts of the code.

I just can't find a better way at the moment, but I am sure there is.



Here is a smaller version of the structure( It will have more than 200 members when finished):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
struct B737_LVAR_Data_1
{
	double A320_Neo_MFD_RANGE_1;
	double A320_Neo_MFD_NAV_MODE_1;
	double A320_Neo_MFD_MAP_MODE_1;

	double XMLVAR_Baro_Selector_HPA_1;
	double XMLVAR_Baro1_Mode;


	double BTN_TERRONND_1_ACTIVE;
	double XMLVAR_NAV_AID_SWITCH_L1_State;
	double XMLVAR_NAV_AID_SWITCH_L2_State;



	double A32NX_SWITCH_TCAS_Position;
	double XMLVAR_RMP_L_NAV_Active;
	double XMLVAR_RMP_R_NAV_Active;
	double RADIONAV_SOURCE;
	double RADIONAV_ACTIVE;
	double XMLVAR_RMP_L_On;
	double XMLVAR_RMP_L_GROUP_FREQ_IS_DISABLED;
	double XMLVAR_RMP_L_CURRENT_BUTTON_IN_GROUP_MODE;

	double A32NX_METRIC_ALT_TOGGLE;

	double A32NX_TRK_FPA_MODE_ACTIVE;


	double A32NX_FCU_SPD_MANAGED_DOT;
	double A32NX_FCU_SPD_MANAGED_DASHES;
	double A32NX_FCU_HDG_MANAGED_DOT;
	double A32NX_FCU_HDG_MANAGED_DASHES;
	double A32NX_FCU_ALT_MANAGED;
	double A32NX_FCU_VS_MANAGED;
	double A32NX_FCU_LOC_MODE_ACTIVE;
	double A32NX_FCU_APPR_MODE_ACTIVE;
	double A32NX_FMA_EXPEDITE_MODE;
	double A32NX_AUTOPILOT_1_ACTIVE;
	double A32NX_AUTOPILOT_2_ACTIVE;
	double A32NX_AUTOTHRUST_STATUS;  // 0 = Disengade, 1= Armed, 2 = Active

};




This function is read 30 times in a second

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
void ReadLVars_737_1()
{
	sB737_LVAR_Data_1.A320_Neo_MFD_RANGE_1 = UpdateLVars("B737_Neo_MFD_RANGE_1");
	sB737_LVAR_Data_1.A320_Neo_MFD_NAV_MODE_1 = UpdateLVars("B737_Neo_MFD_NAV_MODE_1");
	sB737_LVAR_Data_1.A320_Neo_MFD_MAP_MODE_1 = UpdateLVars("B737_Neo_MFD_MAP_MODE_1");

	sB737_LVAR_Data_1.A32NX_METRIC_ALT_TOGGLE = UpdateLVars("A32NX_METRIC_ALT_TOGGLE");
	sB737_LVAR_Data_1.A32NX_SWITCH_TCAS_Position = UpdateLVars("A32NX_SWITCH_TCAS_Position");

	sB737_LVAR_Data_1.XMLVAR_Baro_Selector_HPA_1 = UpdateLVars("XMLVAR_Baro_Selector_HPA_1");
	sB737_LVAR_Data_1.XMLVAR_Baro1_Mode = UpdateLVars("XMLVAR_Baro1_Mode");

	sB737_LVAR_Data_1.XMLVAR_RMP_L_NAV_Active = UpdateLVars("XMLVAR_RMP_L_NAV_Active"); 
	sB737_LVAR_Data_1.XMLVAR_RMP_R_NAV_Active = UpdateLVars("XMLVAR_RMP_R_NAV_Active"); 
	sB737_LVAR_Data_1.XMLVAR_NAV_AID_SWITCH_L1_State = UpdateLVars("XMLVAR_NAV_AID_SWITCH_L1_State"); 
	sB737_LVAR_Data_1.XMLVAR_NAV_AID_SWITCH_L2_State = UpdateLVars("XMLVAR_NAV_AID_SWITCH_L2_State");


	sB737_LVAR_Data_1.RADIONAV_SOURCE = UpdateLVars("RADIONAV_SOURCE"); 
	sB737_LVAR_Data_1.RADIONAV_ACTIVE = UpdateLVars("RADIONAV_ACTIVE"); 


	sB737_LVAR_Data_1.XMLVAR_RMP_L_On = UpdateLVars("XMLVAR_RMP_L_On"); 
	sB737_LVAR_Data_1.XMLVAR_RMP_L_GROUP_FREQ_IS_DISABLED = UpdateLVars("XMLVAR_RMP_L_GROUP_FREQ_IS_DISABLED"); 
	sB737_LVAR_Data_1.XMLVAR_RMP_L_CURRENT_BUTTON_IN_GROUP_MODE = UpdateLVars("XMLVAR_RMP_L_CURRENT_BUTTON_IN_GROUP_MODE"); 

	sB737_LVAR_Data_1.A32NX_TRK_FPA_MODE_ACTIVE = UpdateLVars("A32NX_TRK_FPA_MODE_ACTIVE"); 
	sB737_LVAR_Data_1.A32NX_FCU_SPD_MANAGED_DOT = UpdateLVars("A32NX_FCU_SPD_MANAGED_DOT"); 
	sB737_LVAR_Data_1.A32NX_FCU_SPD_MANAGED_DASHES = UpdateLVars("A32NX_FCU_SPD_MANAGED_DASHES"); 
	sB737_LVAR_Data_1.A32NX_FCU_HDG_MANAGED_DOT = UpdateLVars("A32NX_FCU_HDG_MANAGED_DOT"); 
	sB737_LVAR_Data_1.A32NX_FCU_HDG_MANAGED_DASHES = UpdateLVars("A32NX_FCU_HDG_MANAGED_DASHES"); 
	sB737_LVAR_Data_1.A32NX_FCU_ALT_MANAGED = UpdateLVars("A32NX_FCU_ALT_MANAGED"); 
	sB737_LVAR_Data_1.A32NX_FCU_VS_MANAGED = UpdateLVars("A32NX_FCU_VS_MANAGED"); 
	sB737_LVAR_Data_1.A32NX_FCU_LOC_MODE_ACTIVE = UpdateLVars("A32NX_FCU_LOC_MODE_ACTIVE"); 
	sB737_LVAR_Data_1.A32NX_FCU_APPR_MODE_ACTIVE = UpdateLVars("A32NX_FCU_APPR_MODE_ACTIVE"); 
	sB737_LVAR_Data_1.A32NX_FMA_EXPEDITE_MODE = UpdateLVars("A32NX_FMA_EXPEDITE_MODE"); 
	sB737_LVAR_Data_1.A32NX_AUTOPILOT_1_ACTIVE = UpdateLVars("A32NX_AUTOPILOT_1_ACTIVE"); 
	sB737_LVAR_Data_1.A32NX_AUTOPILOT_2_ACTIVE = UpdateLVars("A32NX_AUTOPILOT_2_ACTIVE"); 
	sB737_LVAR_Data_1.A32NX_AUTOTHRUST_STATUS = UpdateLVars("A32NX_AUTOTHRUST_STATUS"); 


	SimConnect_SetClientData(g_hSimConnect, B737_LVAR_Data_ID_1, B737_LVAR_Data_DEFINITION_1, 0, 0, sizeof(B737_LVAR_Data_1), &sB737_LVAR_Data_1);
}


Here is my UpdateLVars function
1
2
3
4
5
6
7
double UpdateLVars(std::string name) {

	float value = 0;
		int id = check_named_variable(name.c_str());
		value = get_named_variable_value(id);
	return value;
}




Last edited on
If they are always treated exactly the same maybe you could use the "X macro" technique.
https://en.wikipedia.org/wiki/X_Macro

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
double UpdateLVars(const char*);

#define LIST_OF_MEMBERS \
	X(A320_Neo_MFD_RANGE_1) \
	X(A320_Neo_MFD_NAV_MODE_1) \
	X(A320_Neo_MFD_MAP_MODE_1) \
	X(XMLVAR_Baro1_Mode)

struct B737_LVAR_Data_1
{
	#define X(name) double name;
	LIST_OF_MEMBERS
	#undef X
};

void ReadLVars_737_1(B737_LVAR_Data_1& sB737_LVAR_Data_1)
{
	#define X(name) sB737_LVAR_Data_1.name = UpdateLVars(#name);
	LIST_OF_MEMBERS
	#undef X
}
Last edited on
Peter87: This does not work. I get an compile error when using LIST_OF_MEMBERS in the ReadVars_737 function. Another issue is that I need to have a clean data structure definition.
Using X(name) in the structure is not accepted in other parts of my code that uses the structure.

I guess I have to use my original code as no working solutions have been found.
This does not work. I get an compile error when using LIST_OF_MEMBERS in the ReadVars_737 function.

The code I posted compiles. Without the code, or at least the error messages, I can't say why it doesn't work for you.

Another issue is that I need to have a clean data structure definition.
Using X(name) in the structure is not accepted in other parts of my code that uses the structure.

In that case I'm afraid I see no other way than to just list all the members.

There are plans to add static reflection to C++. I think that would allow you to loop over all the members of a struct (at compile time) and generate the initialization code the way you want. Unfortunately that feature is still several years away.
Last edited on
Peter87 wrote:
There are plans to add static reflection to C++.


None of the following is standard c++

There is an implementation of clang++ 14.0 that has reflection (it does the Reflection TS), IIRC one has to build the code with that feature turned on.

https://github.com/matus-chochlik/llvm-project/tree/reflection

One can use it through godbolt compiler explorer for toy projects if one doesn't want to build it.

There are various hits for various levels of reflection and hacks to be found on the web.


OK, Thanks for all feeddback. I will just use my code as other solution seems to complicated or not possiblein respect to other parts of my code.

My code works fine, but as a hobby programmer it looks non-professional to have to write several hundred more or less similar code lines when the only difference between all code lines in the ReadLVars function is the structure member name.
Here is a smaller version of the structure( It will have more than 200 members when finished)


Why do you have to use a struct at all?

If you are in C++, which you obviously are, then consider using a std::unordered_map instead!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <string>
#include <unordered_map>

typedef std::unordered_map<std::string, double> data_t;

void update_variable(data_t &data, std::string name)
{
    const int id = check_named_variable(name.c_str());
    data.insert(std::make_pair(name, get_named_variable_value(id)));
}

void read_variables(data_t &data)
{
    update_variable(data, "B737_Neo_MFD_RANGE_1");
    update_variable(data, "B737_Neo_MFD_NAV_MODE_1");
    update_variable(data, "B737_Neo_MFD_MAP_MODE_1");
    /* ... */
}

int main(void)
{
    data_t my_data;
    read_variables(my_data);
}



Even better, let's define the variable names at one single place:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static const char *VAR_NAMES[] =
{
    "B737_Neo_MFD_RANGE_1",
    "B737_Neo_MFD_NAV_MODE_1",
    "B737_Neo_MFD_MAP_MODE_1",
    /* ... */
    NULL // <-- terminating NULL is important!
}

void read_variables(data_t &data)
{
    for (size_t i = 0; VAR_NAMES[i]; ++i)
    {
        update_variable(data, VAR_NAMES[i]);
    }
}



See also:
https://www.cplusplus.com/reference/unordered_map/unordered_map/
Last edited on
I have removed this post because I failed to consider all the requirements.
Last edited on
What is the value returned from check_named_variable() ? Is this a simple int in the range 0 to N - 1?

If yes (or can be adjusted to be in that range), then possibly something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct B737_LVAR_Datas_1
{
    double vars[N] {};

    double& A320_Neo_MFD_RANGE_1 = vars[0];
    double& A320_Neo_MFD_NAV_MODE_1 = vars[1];
    double& A320_Neo_MFD_MAP_MODE_1 = vars[2];

    double& XMLVAR_Baro_Selector_HPA_1 = vars[3];
    double& XMLVAR_Baro1_Mode = vars[4];
     //....
};

void ReadLVars_737_1()
{
    for (int i = 0; i < N; ++i)
        sB737_LVAR_Data_1.vars[i] = get_named_variable_value(i);
}


Using an array to store the values and having the required individual names as refs to the appropriate array element. The variables then be either accessed via index onto the array or by their name.

Last edited on
kigar64551 wrote:
NULL // <-- terminating NULL is important!

Why?

You can still loop over the array without a null at the end.

Regular for loop:
1
2
3
4
for (size_t i = 0; i < std::size(VAR_NAMES); ++i)
{
    update_variable(data, VAR_NAMES[i]);
}

Range-based for loop:
1
2
3
4
for (const char* var_name : VAR_NAMES)
{
    update_variable(data, var_name);
}
Last edited on
I would consider an enum of your 200+ names.
you can use that to access the particular array location, or in a map, or various other ways.

the C way:
enum things{red,green,blue,maxthings};
double many[maxthings]; //you can typedef this if you want or put into struct
...
cin >> many[blue]; //example usage

update(red);// ... etc

note that this bypasses the "" string variable name idea entirely. You asked for efficient, and if its in the code, maybe you can use the variable name instead of a string (faster comparison)? If you must do a string, this will not work: enums are only ints.


if you end up using your 200 variable code anyway:
- you can write a little program on the side to produce the code you need... Ive had to do this to generate larger lookup tables, the code produces cut and paste c++ that goes in the real program.
- and you can use smarter editors, like notepad++, that let you edit 200 lines at once, so if you pasted a column of the variable names, you can then make them all = 0; or whatever with 3 keystrokes, or a macro that you hold down.
Last edited on
kigar64551:
Why do you have to use a struct at all?


I need the structure for this function:

SimConnect_SetClientData(g_hSimConnect, B737_LVAR_Data_ID_1, B737_LVAR_Data_DEFINITION_1, 0, 0, sizeof(B737_LVAR_Data_1), &sB737_LVAR_Data_1);

This function tells the client software on the other end that there has been changes to the structure, which is then read by the client. The structure in the client is as the structure in my example. That is why I need this specific structure layout.

}


seeplus:
What is the value returned from check_named_variable() ? Is this a simple int in the range 0 to N - 1?


Yes
Does SimConnect_SetClientData() simply take a pointer to a "data" buffer and the size of that buffer? If so, you could write a simple function that serializes the std::unordered_map into a sequence of bytes. On the other side, you used a similar function that de-serializes the retrieved bytes into a std::unordered_map again.

Another option would be to "simulate" a key-to-double map like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#define B737_Neo_MFD_RANGE_1    0
#define B737_Neo_MFD_NAV_MODE_1 1
#define B737_Neo_MFD_MAP_MODE_1 2
/* ... */
#define MAX_KEY_INDEX 42 // <-- actually max key index *plus one*

typedef struct 
{
    double value[MAX_KEY_INDEX];
}
my_map_t

#define UPDATE_VARIABLE(MAP, KEY) update_variable((MAP), (KEY), #KEY)

void update_variable(my_map_t &map, const size_t index, const char *const name)
{
    const int id = check_named_variable(name);
    map.value[index] = get_named_variable_value(id);
}

void read_variables(my_map_t &map)
{
    UPDATE_VARIABLE(map, B737_Neo_MFD_RANGE_1);    // <-- effectively calls update_variable(map, 0, "B737_Neo_MFD_RANGE_1")
    UPDATE_VARIABLE(map, B737_Neo_MFD_NAV_MODE_1); // <-- effectively calls update_variable(map, 1, "B737_Neo_MFD_NAV_MODE_1")
    UPDATE_VARIABLE(map, B737_Neo_MFD_MAP_MODE_1); // <-- effectively calls update_variable(map, 2, "B737_Neo_MFD_MAP_MODE_1")
    /* ... */
}



NULL // <-- terminating NULL is important!
Why?

Yeah, using std::size would be better in this case, because here we can access the array directly.

(If you need to pass a pointer to the array, then maybe not)
Last edited on
kigar64551:
The SimConnect_SetClientData function is used to write one or more units of data to a client data area.

Syntax
HRESULT SimConnect_SetClientData(
HANDLE hSimConnect,
SIMCONNECT_CLIENT_DATA_ID ClientDataID,
SIMCONNECT_CLIENT_DATA_DEFINITION_ID DefineID,
DWORD Flags,
DWORD dwReserved,
DWORD cbUnitSize,
void* pDataSet
);

Parameters
hSimConnect
[in] Handle to a SimConnect object.
ClientDataID
[in] Specifies the ID of the client data area.
DefineID
[in] Specifies the ID of the client defined client data definition.
Flags
[in] Null, or one or more of the following flags.
Flag Description
SIMCONNECT_CLIENT_DATA_SET_FLAG_TAGGED The data to be set is being sent in tagged format. If this flag is not set then the entire client data area should be replaced with new data. Refer to the pDataSet parameter and SimConnect_RequestClientData for more details on the tagged format.
dwReserved
[in] Reserved for future use. Set to zero.
cbUnitSize
[in] Specifies the size of the data set in bytes. The server will check that this size matches exactly the size of the data definition provided in the DefineID parameter. An exception will be returned if this is not the case.
pDataSet
[in] Pointer to the data that is to be written. If the data is not in tagged format, this should point to the block of client data. If the data is in tagged format this should point to the first tag name (datumID), which is always four bytes long, which should be followed by the data itself. Any number of tag name/value pairs can be specified this way, the server will use the cbUnitSize parameter to determine how much data has been sent.
Topic archived. No new replies allowed.