Author Topic: [C] - Passing a structure member as a parameter to a function  (Read 1157 times)

0 Members and 1 Guest are viewing this topic.

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 5445
  • Country: fr
Re: [C] - Passing a structure member as a parameter to a function
« Reply #25 on: July 21, 2020, 03:01:38 pm »
TLDR; Didn't fully read all the answers. I think, as some have probably suggested, what you are actually after is not a fixed struct, but some kind of dictionary (pairs of key/value), and there seems to be examples of that above. A dictionary can be implemented in various ways. Basically, that can be a table (array/list/whatever) of items, each being a struct containing at least the key and the value. To make the look-up faster, you can either sort the table by keys and do a binary search, or implement some kind of hash table. If the number of different keys is small, don't bother, a simple linear search should do the trick.
 

Offline krish2487

  • Frequent Contributor
  • **
  • Posts: 465
  • Country: dk
Re: [C] - Passing a structure member as a parameter to a function
« Reply #26 on: July 21, 2020, 03:38:39 pm »
Why would you even want a generic code to work with sturctures that have nothing in common with each other?

I totally missed this... I can think of a couple of advantages of doing this.
1. Reuse of a common function block. You can have different data structure but one function to get or set them.
2. Code size.
3. Potentially eliminate errors in duplication.
4. Testability. You can simulate just one block of code with as many inputs as you want.
5. Extensibility. You can modify and change the data structures used elsewhere without changing the underlying logic.
If god made us in his image,
and we are this stupid
then....
 

Offline krish2487

  • Frequent Contributor
  • **
  • Posts: 465
  • Country: dk
Re: [C] - Passing a structure member as a parameter to a function
« Reply #27 on: July 21, 2020, 03:40:23 pm »
TLDR; Didn't fully read all the answers. I think, as some have probably suggested, what you are actually after is not a fixed struct, but some kind of dictionary (pairs of key/value), and there seems to be examples of that above. A dictionary can be implemented in various ways. Basically, that can be a table (array/list/whatever) of items, each being a struct containing at least the key and the value. To make the look-up faster, you can either sort the table by keys and do a binary search, or implement some kind of hash table. If the number of different keys is small, don't bother, a simple linear search should do the trick.

Thank you.. It would appear that is what I am looking for.. I ll work on the suggestions above and post what I can come up with.. and put it up for scrutiny! :-)
If god made us in his image,
and we are this stupid
then....
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 5445
  • Country: fr
Re: [C] - Passing a structure member as a parameter to a function
« Reply #28 on: July 21, 2020, 03:54:29 pm »
TLDR; Didn't fully read all the answers. I think, as some have probably suggested, what you are actually after is not a fixed struct, but some kind of dictionary (pairs of key/value), and there seems to be examples of that above. A dictionary can be implemented in various ways. Basically, that can be a table (array/list/whatever) of items, each being a struct containing at least the key and the value. To make the look-up faster, you can either sort the table by keys and do a binary search, or implement some kind of hash table. If the number of different keys is small, don't bother, a simple linear search should do the trick.

Thank you.. It would appear that is what I am looking for.. I ll work on the suggestions above and post what I can come up with.. and put it up for scrutiny! :-)

That may be, but reading your post just above, I'm not completely sure this is really what you want.
Looks like you want this dictionary approach from a software dev POV, but not necessarily as a feature within the software itself.
One obvious drawback of a dictionary is that accessing "members" will always be slower than directly accessing a struct member in C. You may then have performance issues in your software if it matters (it may not in a given situation, so it's up to you.) Also, if members can have different types, you'll need to store the corresponding "type" along with each "key", and the corresponding data content. And the accessor functions will need to deal with this. Drawback beside complexity, is that you won't get the static checks a compiler can do regarding type compatibility and such.

A mixed approach is possible (I've used it to implement easier-to-maintain file read/write and UI (value modification) on top of data structs.)
You define your structs in a normal way, but create additional dictionaries that refer to the members of the structs. The way you can do that is with pointers, and the offsetof() operator. You need to be very familiar with pointers though. Each dictionary entry would be a key, and a reference to the member of a struct (in the form of its offset from the start of the struct, that you get with offsetof().) You'd also need to define a few macros to help writing the necessary code without too much duplication. Then you can access struct members either by "key", or directly. All of this (pointer arithmetic, offsetof(), macros...) can be a bit frowned upon by some though. Up to you.
« Last Edit: July 21, 2020, 03:59:49 pm by SiliconWizard »
 

Offline krish2487

  • Frequent Contributor
  • **
  • Posts: 465
  • Country: dk
Re: [C] - Passing a structure member as a parameter to a function
« Reply #29 on: July 21, 2020, 07:44:51 pm »
Okay.. I have had some time to read, understand and assimilate the suggestions given above.. and I though I will start off with the simplest solution, for now.

The offsets seem like a pretty good solution to achieve most of what I am looking for.
So.. I tried to see if I can come up with how to make a sort of generic function using offsets, and this is what I could think of.

Code: [Select]
typedef struct foo
{
uint8_t  alpha;
uint8_t  bravo;
uint8_t  charlie;
} foo_t;

enum foo_t_keys { alpha = offsetof(foo_t, alpha), bravo = offsetof(foo_t, bravo), charlie = offsetof(foo_t, charlie)  };

typedef struct foo2
{
uint8_t  delta;
uint8_t  echo;
uint8_t  foxtrot;
} foo2_t;

enum foo2_t_keys { delta = offsetof(foo2_t, delta), echo = offsetof(foo2_t, echo), foxtrot = offsetof(foo2_t, foxtrot)  };

void setter (void *s, uint32_t offset, int value)
{
int *ptr = (int *)((uint8_t *)s + offset);
*ptr = value;
}

int main()
{
foo_t foo;
foo2_t foo2;

setter(&foo, alpha, 100);
setter(&foo, bravo, 20);
setter(&foo, charlie, 30);

setter(&foo2, delta, 250);
setter(&foo2, echo, 200);
setter(&foo2, foxtrot, 150);

printf("Value of foo.alpha is %d\n", foo.alpha);
printf("Value of foo.bravo is %d\n", foo.bravo);
printf("Value of foo.charlie is %d\n", foo.charlie);

printf("Value of foo2.delta is %d\n", foo2.delta);
printf("Value of foo2.echo is %d\n", foo2.echo);
printf("Value of foo2.foxtrot is %d\n", foo2.foxtrot);

return 0;
}

Basically have a standard C structure and an enum mapping the keys to the offsets. This only works if the enum is in sync with the structure.
and have a generic setter function that takes a structure (void*) , offset (uint32_t) and value (uint8_t) as the arguments.
The above example compiles fine and works.

One huge caveat is that it only works if the structure is made up of the same kind of data type members. I am still thinking of how to modify the setter accept any kind of data type for the value and cast it internally using a lookup..

Still a WIP for the different types of values.. I might need to have another enum(in sync with the structure) tracking the data types and have some sort of lookup to cast the argument structure and the offset... I m guessing this might require some macro magic...
« Last Edit: July 21, 2020, 07:47:29 pm by krish2487 »
If god made us in his image,
and we are this stupid
then....
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 6602
  • Country: us
    • Personal site
Re: [C] - Passing a structure member as a parameter to a function
« Reply #30 on: July 21, 2020, 07:50:30 pm »
I don't get it. If the name of the member is not a string and known at compile time (like in the code above), then a simple macro will work:
Code: [Select]
#define setter(s, member, value) (s)->member = value
or depending on the way struct is passed
#define setter(s, member, value) (s).member = value

But I don't see how is this helpful at all.
Alex
 
The following users thanked this post: krish2487

Offline krish2487

  • Frequent Contributor
  • **
  • Posts: 465
  • Country: dk
Re: [C] - Passing a structure member as a parameter to a function
« Reply #31 on: July 21, 2020, 08:02:20 pm »
I don't get it. If the name of the member is not a string and known at compile time (like in the code above), then a simple macro will work:
Code: [Select]
#define setter(s, member, value) (s)->member = value
or depending on the way struct is passed
#define setter(s, member, value) (s).member = value

But I don't see how is this helpful at all.

You are indeed right.. It works.. and even for strings.. Looks like I wasted mine and a lot of fourm members time for very little returns. I apologize for this.
The members are known at compile time. I think simply maintaining a enum in sync with the actual struct members should solve what I was looking for in the first place. 

Thank you @ataradov. This is indeed simple and valuable! :-)

PS: A similar macro works even for a getter function.. for strings.. bools.. ints.. as long as the struct in question has the member defined.. the macro will work.. if not.. then it throws a compilation error.
« Last Edit: July 21, 2020, 08:17:04 pm by krish2487 »
If god made us in his image,
and we are this stupid
then....
 

Offline grumpydoc

  • Super Contributor
  • ***
  • Posts: 2746
  • Country: gb
Re: [C] - Passing a structure member as a parameter to a function
« Reply #32 on: July 22, 2020, 12:51:20 pm »
The general idea of being able to take a structure definition such as

struct foo {
   sometype_1_t bar;
   sometype_2_t baz;
   sometype_3_t frob;
   sometype_4_t fizzle;
};


and be able, at runtime, to get info about the name and type of each element and to access the elements using a string containing the field name falls under the general heading of "type introspection" (and "reflection" for actual access to the variables rather than just type info).

It can be useful if you want to write generic code to, say, pick up configuration parameters from a text file but have regular data structures in your program rather than key-value dictionaries.

Some languages have introspection and reflection built in, but not C, you can do something manual and play preprocessor games to help build the necessary data structures to make it work and perhaps even somewhat type-safe but it will always be a question of "rolling your own".


 

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 3516
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: [C] - Passing a structure member as a parameter to a function
« Reply #33 on: July 22, 2020, 12:55:56 pm »
I did that once, but quickly dropped it due to the risks (wrong type, unaligned access, and other manual errors)
Looked like this:
Code: [Select]
struct item {
  void* base;
  uint16_t offset;     
  enum type;         
}
#define item_init(base, element, type) { &base, offsetof(base, element), type }

You can always make a metacompiler, like qmake, that creates such list.
For example using: https://pypi.org/project/pycparser/
 

Offline krish2487

  • Frequent Contributor
  • **
  • Posts: 465
  • Country: dk
Re: [C] - Passing a structure member as a parameter to a function
« Reply #34 on: July 22, 2020, 01:33:18 pm »
The general idea of being able to take a structure definition such as

struct foo {
   sometype_1_t bar;
   sometype_2_t baz;
   sometype_3_t frob;
   sometype_4_t fizzle;
};


and be able, at runtime, to get info about the name and type of each element and to access the elements using a string containing the field name falls under the general heading of "type introspection" (and "reflection" for actual access to the variables rather than just type info).

It can be useful if you want to write generic code to, say, pick up configuration parameters from a text file but have regular data structures in your program rather than key-value dictionaries.

Some languages have introspection and reflection built in, but not C, you can do something manual and play preprocessor games to help build the necessary data structures to make it work and perhaps even somewhat type-safe but it will always be a question of "rolling your own".

That is the sort of use case I am referring to.. an datalogger that powers up.. reads a configuration file.. acquires/ processess the data, sends to server.. rewrites the configuration parameters to a file and sleeps..

While it sounds convoluted, about the reading and rewriting the configuration parameters back, it has a good reason. The self test performed at the beginning of every cycle store the results during the iteration lifetime in memory.. and any sensors that fail the ST are written as such to the configuration file to a non volatile storage.. On next boot, they would not even be attempted since they would / should have been disabled in the previous write to file.

However, it is much much easier to read a config file into a a traditional C structure and do any operations on them instead of constantly looking up a key value in a dictionary or file.. hence the first step of reading the configuration file, parsing the contents and constructing a structure to hold the parsed values.. It is here that the runtime construction of a structure and its associated type, length considerations came into play.
If god made us in his image,
and we are this stupid
then....
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 1775
  • Country: fi
    • My home page and email address
Re: [C] - Passing a structure member as a parameter to a function
« Reply #35 on: July 22, 2020, 01:42:41 pm »
In Linux, one can use ELF sections to do the heavy lifting:
Code: [Select]
#include <stdlib.h>
#include <stdint.h>
#include "conf.h"

struct foo {
    int x;
    int y;
    float z;
};

/* Generic structure operations, use when declaring structures of this type */
CONFIG_OPS(struct_foo_ops) = {
    CONFIG_OP("x", offsetof(struct foo, x), CONFIG_TYPE_INT),
    CONFIG_OP("y", offsetof(struct foo, y), CONFIG_TYPE_INT),
    CONFIG_OP("z", offsetof(struct foo, z), CONFIG_TYPE_FLOAT),
    CONFIG_OPS_END
};

/* An instance of above configurable structure */
struct foo bar;
CONFIG_STRUCT("bar", &bar, struct_foo_ops);

/* Another instance of above configurable structure */
struct foo baz;
CONFIG_STRUCT("baz", &baz, struct_foo_ops);
so that at runtime,
Code: [Select]
    CONFIG_FOREACH(st) {
        printf("Structure \"%s\" at %p has:\n", st->name, st->base);
        CONFIG_FOREACH_OP(op, st) {
            printf("  Member \"%s\" at %p (type %d)\n", op->name, (void *)(op->addr + (uintptr_t)st->base), op->type);
        }
    }
prints
Code: [Select]
Structure "baz" at 0x55c7b19f0068 has:
  Member "x" at 0x55c7b19f0068 (type 1)
  Member "y" at 0x55c7b19f006c (type 1)
  Member "z" at 0x55c7b19f0070 (type 2)
Structure "bar" at 0x55c7b19f0078 has:
  Member "x" at 0x55c7b19f0078 (type 1)
  Member "y" at 0x55c7b19f007c (type 1)
  Member "z" at 0x55c7b19f0080 (type 2)
but I've never tried to use this on a microcontroller.  I do believe it works using avr-gcc and arm-gcc on Linux (that is, compiling on any Linux machine to AVR or ARM targets using GCC), just haven't done it.  (The key is that one must compile an ELF binary for the target, you see.  This facility is an integral part of the ELF file format.)

Essentially, each CONFIG_OPS(varname) defines an array of struct config_op's,
Code: [Select]
#define  CONFIG_TYPE_INT    1
#define  CONFIG_TYPE_FLOAT  2

struct config_op {
    const unsigned char *name;
    uintptr_t            addr;
    uint_fast8_t         type;
};
and each CONFIG_STRUCT(strname, addr, ops) declares a static const variable (with a name generated based on the line number) emitted to a special section; I used config_ops:
Code: [Select]
struct config_struct {
    const unsigned char    *name;
    void                   *base;
    const struct config_op *ops;
    void                   *padding;
};
The structure size does need to have size a multiple of section alignment (you can adjust that using a linker script; I just padded the structure).  GCC helpfully provides external symbols __start_config_ops and __stop_config_ops (as long as the section name is suitable for a C variable name), whose addresses (&__start_config_ops and &__stop_config_ops) span the variables emitted to this section in all compilation units.

The macro-shenanigans are fairly ugly, and you do need to link the object files to an executable, but this mechanism is useful for link-time merging of entries into a single contiguous array.  It's used fairly often in the Linux kernel, for example.  Also, the ELF constructors/destructors (executed before and after main()) use a similar mechanism, with .init_array and .fini_array sections containing the addresses of the functions to be executed.

The annoying part of this kind of scheme is still to write the accessor functions; i.e., conversion to and from string, when given a pointer to the target variable and its type number.

Overall, the ROM overhead is two pointers and one/two integers per member in a modifiable structure type, plus two pointers and one/two integers for the terminating marker.  The second integer is for a hash (I recommend DJB2 xor variant), to speed up string lookups.  Each modifiable structure of any type requires three pointers plus an optional integer, for hashing the name.  Plus the string literals for all the names.

So, in general, this approach would work very well if you had many instances of the same type of structures that can be accessed via their names.  (Parsing dot-separated names like foo.x to locate the target member is easy.)
« Last Edit: July 22, 2020, 01:46:46 pm by Nominal Animal »
 

Online Mechatrommer

  • Super Contributor
  • ***
  • Posts: 9766
  • Country: my
  • reassessing directives...
Re: [C] - Passing a structure member as a parameter to a function
« Reply #36 on: July 22, 2020, 02:01:52 pm »
For sake of completeness
This is one such data structure I have.

Code: [Select]
typedef struct GPS_Payload{

char* Device_Token;
float Latitude;
float Longitude;
float Altitude;
uint8_t Fix_Type;

} Payload;
and this is another
Code: [Select]
typedef struct Dev_Settings{
bool PMS5003_Enabled;
bool ECC508_Enabled;
bool ADS1015_Enabled;
char* Device_Token;
uint8_t Interval;

} SensorSettings;

I am basically, trying to make an easy job hard and come up with a way of making a generic function that can take pointers to (either) structures, structure members and values to set as arguments and operate accordingly.
your question is still unclear... the only common thing between Payload and SensorSettings is member Device_Token. so i assume you want to operate/modify this member in a generic function? you are asking for disaster esp the way you arrange the members order like that. what you seek probably is class (or structure) inheritance or polymorphism in OOP.

btw: stupidity is the lack of intelligence. each goes in the other direction of the other. both if summed equal to 100%... 46% intelligence means there is 54% stupidity left. 100% intelligence means 0% stupidity left. so if the 100% intelligence matter made something from His image, so we can safely assume the image is only has 1% intelligence, so guess what the 99% is? cheers.
« Last Edit: July 22, 2020, 02:17:54 pm by Mechatrommer »
It's extremely difficult to start life.. one features of nature.. physical laws are mathematical theory of great beauty... You may wonder Why? our knowledge shows that nature is so constructed. We simply have to accept it. One could describe the situation by saying that... (Paul Dirac)
 

Online T3sl4co1l

  • Super Contributor
  • ***
  • Posts: 15435
  • Country: us
  • Expert, Analog Electronics, PCB Layout, EMC
    • Seven Transistor Labs
Re: [C] - Passing a structure member as a parameter to a function
« Reply #37 on: July 22, 2020, 02:07:28 pm »
The usual way of parsing config files is to simply hand write every option.  It's known at compile time, after all.  This can be streamlined into matching lines to strings (from an array) and getting pointers/offsets and types from there.  Which ends up very similar to what we have above, so that checks out.

The simpler, MCU-resident way to do what you describe, would be to just store some flags in EEPROM and check them before testing the respective sensor.

Tim
Seven Transistor Labs, LLC
Electronic design, from concept to prototype.
Bringing a project to life?  Send me a message!
 

Offline madires

  • Super Contributor
  • ***
  • Posts: 5319
  • Country: de
  • A qualified hobbyist ;)
Re: [C] - Passing a structure member as a parameter to a function
« Reply #38 on: July 22, 2020, 02:08:40 pm »
Interesting ideas! But who wants to maintain source code with so much complexity?
 

Offline krish2487

  • Frequent Contributor
  • **
  • Posts: 465
  • Country: dk
Re: [C] - Passing a structure member as a parameter to a function
« Reply #39 on: July 22, 2020, 02:41:18 pm »
For sake of completeness
This is one such data structure I have.

Code: [Select]
typedef struct GPS_Payload{

char* Device_Token;
float Latitude;
float Longitude;
float Altitude;
uint8_t Fix_Type;

} Payload;
and this is another
Code: [Select]
typedef struct Dev_Settings{
bool PMS5003_Enabled;
bool ECC508_Enabled;
bool ADS1015_Enabled;
char* Device_Token;
uint8_t Interval;

} SensorSettings;

I am basically, trying to make an easy job hard and come up with a way of making a generic function that can take pointers to (either) structures, structure members and values to set as arguments and operate accordingly.
your question is still unclear... the only common thing between Payload and SensorSettings is member Device_Token. so i assume you want to operate/modify this member in a generic function? you are asking for disaster esp the way you arrange the members order like that. what you seek probably is class inheritance or polymorphism in OOP.

btw: stupidity is the lack of intelligence. each goes in the other direction of the other. both if summed equal to 100%... 46% intelligence means there is 54% stupidity left. 100% intelligence means 0% stupidity left. so if the 100% intelligence matter made something from His image, so we can safely assume the image is only has 1% intelligence, so guess what the 99% is? cheers.

For now I am good. ataradovs macros did the job!! :-)
You are absolutely right in that I am looking at polymorphism type of programming in C.
I gave those structures simply because they were  the most easiest to show. I ll try and explain what I set out to do.. I have a bunch of structures, with different members and different member types. I want to have a generic setter function that takes a structure, structure member and value and then sets the specified member to the passed in value. similarly a getter function. And as it turns out.. I was overcomplicating and a string comparision would do the job just fine and probably more easy to implement.

Quote
btw: stupidity is the lack of intelligence. each goes in the other direction of the other. both if summed equal to 100%... 46% intelligence means there is 54% stupidity left. 100% intelligence means 0% stupidity left. so if the 100% intelligence matter made something from His image, so we can safely assume the image is only has 1% intelligence, so guess what the 99% is? cheers.

It was meant as a wry attempt at humour.. not intended to hurt the sentiments of anyone..

The usual way of parsing config files is to simply hand write every option.  It's known at compile time, after all.  This can be streamlined into matching lines to strings (from an array) and getting pointers/offsets and types from there.  Which ends up very similar to what we have above, so that checks out.

The simpler, MCU-resident way to do what you describe, would be to just store some flags in EEPROM and check them before testing the respective sensor.

Tim

Yes that is how I started.  An external flash is used to store some device logs.. so made sense to move the settings offboard from the MCU as well. Hence the entire exercise..

Interesting ideas! But who wants to maintain source code with so much complexity?
Interesting indeed. I have to I suppose... since it is a side project of mine.. :-P
If god made us in his image,
and we are this stupid
then....
 

Online Mechatrommer

  • Super Contributor
  • ***
  • Posts: 9766
  • Country: my
  • reassessing directives...
Re: [C] - Passing a structure member as a parameter to a function
« Reply #40 on: July 22, 2020, 02:53:20 pm »
For now I am good. ataradovs macros did the job!! :-)
You are absolutely right in that I am looking at polymorphism type of programming in C.
I gave those structures simply because they were  the most easiest to show. I ll try and explain what I set out to do.. I have a bunch of structures, with different members and different member types. I want to have a generic setter function that takes a structure, structure member and value and then sets the specified member to the passed in value. similarly a getter function. And as it turns out.. I was overcomplicating and a string comparision would do the job just fine and probably more easy to implement.
or probably "function template" feature in C++. with string comparison, you'll have to expand/modify your generic function (setter or getter regardless) as your number of structures is expanding. with built in language features, all your comparisons, virtual tables and function expansions is taken cared of behind the curtain, just dont expect fancy codes can run accordingly in nonfancy machines however you try to squeeze it.
It's extremely difficult to start life.. one features of nature.. physical laws are mathematical theory of great beauty... You may wonder Why? our knowledge shows that nature is so constructed. We simply have to accept it. One could describe the situation by saying that... (Paul Dirac)
 

Offline grumpydoc

  • Super Contributor
  • ***
  • Posts: 2746
  • Country: gb
Re: [C] - Passing a structure member as a parameter to a function
« Reply #41 on: July 22, 2020, 04:19:26 pm »
Interesting ideas! But who wants to maintain source code with so much complexity?
If you need it, you need it, so no choice but to maintain it.

But I'd rather maintain a decent hand-rolled introspection/reflection implementation which keeps the complexity in one place (and, if done well, doesn't need to be touched again) than a rag-tag set of enums accessor macros and stuff which needs to be kept carefully in sync with the structure definitions.
 

Offline krish2487

  • Frequent Contributor
  • **
  • Posts: 465
  • Country: dk
Re: [C] - Passing a structure member as a parameter to a function
« Reply #42 on: July 22, 2020, 04:44:55 pm »
you are asking for disaster esp the way you arrange the members order like that. .

Would you mind elaborating on that? Is there something wrong with the way my structures are laid out?
I know it is not the most elegant, but I do not see something obviously wrong with it.
If god made us in his image,
and we are this stupid
then....
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 5445
  • Country: fr
Re: [C] - Passing a structure member as a parameter to a function
« Reply #43 on: July 22, 2020, 04:57:17 pm »
Keep in mind that if access performance is not a key issue, you could still implement some kind of dictionary and do without C structs altogether, which would require less messing around. Also, there are multiple libraries for that out there. That would basically be a mini-database.
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2296
  • Country: nz
Re: [C] - Passing a structure member as a parameter to a function
« Reply #44 on: July 27, 2020, 09:03:56 am »
I have two takes...

Option 1
Linked list, maybe with hashing to speed up accces
Code: [Select]
struct key_value_pair {
   struct key_value_pair *next;
   char *key;
   char *value;   
};

and in the top level structure...
Code: [Select]
struct structure {
   struct key_value_pair *first_keyvalue; 
   ... other members ...
}

Good points - Can have as many keys and values as you want, with values of unlimited size (well, as much as memory allows)

Bad points - Need to use dynamic memory allocation, so memory leaks are possible.

Option 2

If you know the maximum count and sizes of key+value pairs you might have, then...

Code: [Select]
struct key_value_pair {
   char key[MAX_KEY_LEN+1];
   char value[MAX_VALUE_LEN+1];   
};

struct structure {
   struct key_value_pair key_value_pairs[MAX_KEY_VALUE_PAIRS];
      ... other members ...
};

Pros: Avoids dynamic memory allocation.

Cons: All memory for the maximum case needs to be allocated up front. Hard to speed up.

In the latter case adding, searching, updating are relatively simple, with the only error cases of 'key' or 'value' being too long, or if MAX_KEY_VALUE_PAIRS is exceeded.

If you code your own linked lists there are a few different corner cases to deal with (performing operations on an empty list, performing operations on the head of the list, performing operations in the body of the list, performing operations on the tail), along with the chance of running out of memory or memory fragmentation. You will write buggy list code until you get the pattern fixed in your head.
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Offline krish2487

  • Frequent Contributor
  • **
  • Posts: 465
  • Country: dk
Re: [C] - Passing a structure member as a parameter to a function
« Reply #45 on: July 28, 2020, 09:07:41 am »
Keep in mind that if access performance is not a key issue, you could still implement some kind of dictionary and do without C structs altogether, which would require less messing around. Also, there are multiple libraries for that out there. That would basically be a mini-database.

It is going to run on a STM32F1 micro.. I am guessing it is going to be of some concern. The overall firmware has very little timing constraints. I would prefer to stick to a C struct primarily for its simplicity.

I have two takes...

Option 1
Linked list, maybe with hashing to speed up accces
Code: [Select]
struct key_value_pair {
   struct key_value_pair *next;
   char *key;
   char *value;   
};

and in the top level structure...
Code: [Select]
struct structure {
   struct key_value_pair *first_keyvalue; 
   ... other members ...
}


Option 2

If you know the maximum count and sizes of key+value pairs you might have, then...

Code: [Select]
struct key_value_pair {
   char key[MAX_KEY_LEN+1];
   char value[MAX_VALUE_LEN+1];   
};

struct structure {
   struct key_value_pair key_value_pairs[MAX_KEY_VALUE_PAIRS];
      ... other members ...
};

The first option looks really straightforward. However, as I have mentioned earlier, ataradovs macros sort of addressed the requirement.

Thank you @hamzter_nz and @siliconwizard for your suggestions. :-)


One a seperate note, ataradovs answer provided with a basic set of macros to do the job. I ran into some other issues while trying to mock / unit test the macros.. but as a work around  I settled on verifying that the struct member had a changed value pre and post the macro invocation rather than mock the macro itself. That is a seperate thread in and of itself.

So what  I am trying to saying thanks a lot fellows!! :-) It has been a very educative / productive discussion with plenty of helpful suggestions.
If god made us in his image,
and we are this stupid
then....
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 1775
  • Country: fi
    • My home page and email address
Re: [C] - Passing a structure member as a parameter to a function
« Reply #46 on: July 28, 2020, 03:51:09 pm »
You know, if all your configurable items are global variables, or items in global structures, you can use
Code: [Select]
struct config_entry {
    const char *const name;     /* In Flash/ROM */
    void       *const addr;     /* Address of variable in RAM */
    const uint32_t    hash:24;  /* 24-bit hash */
    const uint32_t    type:8;   /* 8-bit type */
};

/* Example types */
#define  TYPE_BIT0    0
#define  TYPE_BIT31   31
#define  TYPE_BIT(n)  (n)
#define  TYPE_BYTE    32
#define  TYPE_INT     45
#define  TYPE_FLOAT   78

static inline uint32_t config_hash(const char *src)
{
    uint32_t  result = 5381;
    while (*src)
        result = (result * 33) ^ (uint32_t)(*(src++));
    return result;
}

/* Example items */
float  origin_x;
float  origin_y;
float  origin_z;
int    speed_v;
struct {
    int a;
    int b;
    int c;
} frob;

/* Configuration array, naming all configurable variables. */
static const struct config_entry  config[] = {
    /* Keep sorted by         vvvvvvv             */
    { "origin.z", &origin_z,  5255461, TYPE_FLOAT },
    { "origin.y", &origin_y,  5255462, TYPE_FLOAT },
    { "origin.x", &origin_x,  5255463, TYPE_FLOAT },
    { "speed.v",  &speed_v,   6516090, TYPE_INT   },
    { "frob.b",   &(frob.b), 15471920, TYPE_INT },
    { "frob.c",   &(frob.c), 15471921, TYPE_INT },
    { "frob.a",   &(frob.a), 15471923, TYPE_INT },
};
The config[] array is in ROM/Flash.  When you add new entries, you do need to calculate the 24-bit hash for the third column; but you can use for example the following hash.py Python script:
Code: [Select]
from sys import argv, stdout, stderr, exit

def hash(string):
    result = 5381
    for c in string:
        result = ((result * 33) ^ ord(c)) & 4294967295
    return result & 16777215

if __name__ == '__main__':
    if len(argv) < 2 or argv[1] in ('-h', '--help', '/?'):
        stderr.write("\n")
        stderr.write("Usage: %s [ -h | --help | /? ]\n" % argv[0])
        stderr.write("       %s STRING [ STRING ... ]\n" % argv[0])
        stderr.write("\n")
        stderr.write("This program computes the 24-bit DJB2 XOR hash\n")
        stderr.write("of the input strings, and outputs them as\n")
        stderr.write("a C hash table array.\n")
        stderr.write("\n")
        exit(0)

    table = []

    for arg in argv[1:]:
        table.append((hash(arg), arg),)

    table.sort()
    for entry in table:
        stdout.write("    { \"%s\", NULL, %8d, 0 },\n" % (entry[1], entry[0]))
At run time, you can use a binary search to rapidly locate the correct configuration item:
Code: [Select]
const struct config_entry *find_config(const char *name)
{
    const uint32_t  hash = config_hash(name);
    const int_fast32_t  n = (sizeof config / sizeof config[0]);
    int_fast32_t  imin = 0, iend = n;
    int_fast32_t  i;

    if (hash < config[0].hash || hash > config[n-1].hash)
        return NULL;

    while (iend > imin) {
        i = imin + (iend - imin)/2;
        if (config[i].hash < hash)
            imin = i;
        else
        if (config[i].hash > hash)
            iend = i;
        else {
            /* Extend imin, imax to cover all matching hashes */
            imin = i;
            while (imin > 0 && config[imin-1].hash == hash)
                imin--;
            iend = i + 1;
            while (iend < n && config[iend].hash == hash)
                iend++;
            break;
    }

    for (i = imin; i < iend; i++)
        if (config[i].hash == hash && !strcmp(name, config[i].name))
            return config + i;

    return NULL;
}
From there, it is just a matter of examining the ->type, and getting or setting the corresponding value from ->addr.

On a 32-bit microcontroller, this takes 13 bytes plus the name string per configurable variable of ROM/flash.

Note that you can integrate the hash calculation to your parser, so that whenever you find an identifier, you have both the identifier string and its hash.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf