Reputation: 9
Given that there are multiple struct typedefs (all different) but with the same first element 'out' as in:
typedef struct myStructOneOfMany
{
uint8_t out;
//somestuff that is different
void* inPntr[33];
struct myStructOneOfMany *prev;
struct myStructOneOfMany *next;
}myStructOneOfMany;
how could one use a generic struct:
typedef struct OUT
{
uint8_t out;
}OUT;
with only the 1st element to access just the 1st element of all the different types?
assume:
myStructOneOfMany *currMyStructOneOfMany = //pointer to 1st in the linked list
I have tried:
uint8_t value = (struct OUT*)(currMyStructOneOfMany->inPntr[4])->out;
... along with other variations and I get nowhere.
Thanks!
Upvotes: 0
Views: 416
Reputation: 9
Well it seems I over thought things. The last comment above compiles and works !
uint8_t value = *(uint8_t *)(…some expression denoting a pointer to a structure with a uint8_t first member…);
I thought I had to make a generic struct similar to my several typedefs.
As Jonathan said ... no need to get fancy. The pointer to the struct is also a pointer to the first element as long as you cast the pointer to point to the same type as the first element.
Thanks to Jonathan Leffler for pointing this out !!
PS: one thing that threw me was the additional * at the beginning. I assumed the cast to (uint8_t*) was enough but I guess this just says "treat the right side after the cast as a pointer to type uint8_t.
Without the * before the cast ... 'value' would get the pointer value ... not the value that the pointer points to. I am still between beginner and intermediate on C but making progress
Thanks to you all !!
Upvotes: 0
Reputation: 19221
EDIT:
As written in Jonathan Leffler's great comment - C11 §6.7.2.1 ¶15 says:
A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa.
Which is why we either use a struct in the first element (matching padding and all), or a single variable / enum
. I prefer using structs because they are easier to extend later on, if you want to add something.
Structs are interesting and complex animals, their field include compiler specific paddings and alignments.
Although very often the code will work fine by a simple matching of the top element, it isn't always the case when you're trying to use a common header with a number of fields (that would be undefined behavior due to padding concerns)... single elements are fine.
A classic way (that is also a compiler correct way) to implement "inheritance" in C would look something like this:
struct parent_s {
/* common attributes */
int object_type; /* for example */
};
struct child_s {
/* must be first element if you want pointers to be interchangeable. */
struct parent_s parent;
/*more stuff */
int my_unique_data;
}
Similar code is used in network address structures.
P.S.
This was a very quick and simplified answer, enough to get you started but not very accurate.
As you explore this you will discover techniques that allow you to place the common structures all over the place and have multiple inheritance when you need it.
I used to miss inheritance in C, but these days I love C better than many OO languages because it gives me more control over the memory structure and I can manage most inheritance features without the need to resort to virtual function tables (which I very rarely do).
EDIT:
Following @AjayBrahmakshatriya 's comment, I thought a quick casting example might be nice... So here's something I'm working on that uses the exact type of inheritance you're asking about (I just started this part of the code today, so it might be glitchy, I also didn't sleep for two days...):
/** Used to identify te type of the RESP object. */
enum resp_type_enum {
/** A String object (`resp_string_s`) that indicates an error. */
RESP_ERR = 0,
/** A simple flag object object (`resp_object_s`) for NULL. */
RESP_NULL,
/** A simple flag object object (`resp_object_s`) for OK. */
RESP_OK,
/** A Number object object (`resp_number_s`). */
RESP_NUMBER,
/** A String object (`resp_string_s`). */
RESP_STRING,
/** An Array object object (`resp_array_s`). */
RESP_ARRAY,
/** A specific Array object object (`resp_array_s`) for Pub/Sub semantics. */
RESP_PUBSUB,
};
/* This is the parent "class" / object */
typedef struct { enum resp_type_enum type; } resp_object_s;
/* an Array child class */
typedef struct {
enum resp_type_enum type;
size_t len;
size_t pos; /** allows simple iteration. */
resp_object_s *array[];
} resp_array_s;
/* a String child class */
typedef struct {
enum resp_type_enum type;
size_t len;
uint8_t string[];
} resp_string_s;
/* a Number child class */
typedef struct {
enum resp_type_enum type;
int64_t number;
} resp_number_s;
I wrote macros that allow me to easily cast from one type to another. They include error checks (by returning NULL if the types don't match:
#define resp_obj2arr(obj) \
((resp_array_s *)((obj)->type == RESP_ARRAY || (obj)->type == RESP_PUBSUB \
? (obj) \
: NULL))
#define resp_obj2str(obj) \
((resp_string_s *)((obj)->type == RESP_STRING || (obj)->type == RESP_ERR \
? (obj) \
: NULL))
#define resp_obj2num(obj) \
((resp_number_s *)((obj)->type == RESP_NUMBER ? (obj) : NULL))
This allows me to do use the macros to easily switch between the different "types".
switch (obj->type) {
case RESP_ERR:
safe_write1('-');
safe_write2((resp_obj2str(obj)->string), (resp_obj2str(obj)->len));
safe_write_eol();
break;
case RESP_NULL:
safe_write2("$-1\r\n", (resp_obj2str(obj)->len));
break;
case RESP_OK:
safe_write2("+OK\r\n", 5);
case RESP_ARRAY:
case RESP_PUBSUB:
safe_write1('*');
safe_write_i(resp_obj2arr(obj)->len);
safe_write_eol();
{
resp_array_s *a = resp_obj2arr(obj);
a->pos = a->len;
obj = NULL;
while (a->pos) {
a->pos--;
push_obj(a->array[a->pos], obj);
obj = a->array[a->pos];
}
}
// ...
}
Upvotes: 2