I have a question related to struct initialization in C.
I have a struct:
struct TestStruct
{
u8 status;
u8 flag1;
u8 flag2;
};
I want a generic function/macro to initialize this struct and set the value of one parameter, eg status =1, easy way is:
TestStruct t = {};
t.status = 1;
However, by doing this I have set the value of status twice, first to 0 in init function and second set it to 1 (optimization does not help?).
(Please dont tell me t = {1,0,0} Im looking for a generic way)
I am thinking about a macro in init function, something like:
#define INIT_TESTSTRUCT (param, value) \
{ .status=0, .flag1=0, .flag2=0, .param=value }
TestStruct t = INIT_TESTSTRUCT(status, 0);
However, the compiler gives error "initialized field overwritten", because I have set the value of status twice.
Please help to point out how to alter the macro to achieve what I want, many thanks.
#define INIT_TESTSTRUCT(param, value) \
{ .param=(value) }
TestStruct t = INIT_TESTSTRUCT(status, 0);
should do it. The variable is then added in .data segment - as it is an initialized one - and all fields which are not mentionned explicitly are set to 0 by the compiler (instead of the linker or the loader).
You have a space in the wrong place:
#define INIT_TESTSTRUCT(param, value) \
{ .status=0, .flag1=0, .flag2=0, .param=(value) }
should do it.
The ( of the macro definition must come immediately after the macro name. Otherwise the parser takes this as a macro with no arguments and expands it to (param, value) ...etc... and in your case this then obviously is a syntax error.
Also note that it is usually a good idea to put () around parameters in the replacment text, to avoid syntax confusion.
Well, firstly
TestStruct t = {};
is illegal in C. If it compiled in your case, it must be a non-standard compiler extension. You can achieve the same effect by
TestStruct t = { 0 };
which is legal in C. It sets all fields in the entire struct to zeros.
Secondly, even with designated initializers C language follows the traditional "all-or-nothing" approach to initialization: if you initialize just one field of the aggregate, all other fields are implicitly initialized to zero.
Which means that in your case all you have to do is
TestStruct t = { .status = 1 };
to initialize the entire struct. status field will be set to 1, while all other fields will be set to zero.
So, your macro can be implemented as
#define INIT_TESTSTRUCT(param, value) { .param = value }
TestStruct t = INIT_TESTSTRUCT(status, 1);
There's no need to explicitly set all other fields to zero - it will happen by itself.
Related
I am new to C and using it to program a Nordic nrf52 chip. I believe my problem is a general C one though rather than application.
I am setting up an array of structs using macros predefined in the chip SDK. Using those macros in the array initialisation works, but doing element by element does not.
So, the following works:
nrf_twi_mngr_transfer_t transfers_1[2] = { \
NRF_TWI_MNGR_WRITE(MSBARO5X_0_ADDR , ®_addr[1], 1, NRF_TWI_MNGR_NO_STOP), \
NRF_TWI_MNGR_READ (MSBARO5X_0_ADDR , &p_buffer[0], sizeof(p_buffer), 0)
};
Where:
typedef struct {
uint8_t * p_data; ///< Pointer to the buffer holding the data.
uint8_t length; ///< Number of bytes to transfer.
uint8_t operation; ///< Device address combined with transfer direction.
uint8_t flags; ///< Transfer flags (see #ref NRF_TWI_MNGR_NO_STOP).
} nrf_twi_mngr_transfer_t;
NRF_TWI_WRITE and _READ are macros that use further macros, for example:
#define NRF_TWI_MNGR_WRITE(address, p_data, length, flags) \
NRF_TWI_MNGR_TRANSFER(NRF_TWI_MNGR_WRITE_OP(address), p_data, length, flags)
which uses
#define NRF_TWI_MNGR_WRITE_OP(address) (((address) << 1) | 0)
and
#define NRF_TWI_MNGR_TRANSFER(_operation, _p_data, _length, _flags) \
{ \
.p_data = (uint8_t *)(_p_data), \
.length = _length, \
.operation = _operation, \
.flags = _flags \
}
What I want to do is change individual items in this array, for example:
transfers_1[0] = NRF_TWI_MNGR_WRITE(MSBARO5X_0_ADDR , ®_addr[1], 1, NRF_TWI_MNGR_NO_STOP);
However when I do that, I get the error "expected an expression".
MSBARO5X_0_ADDR is also defined in a define statement:
#define MSBARO5X_0_ADDR 0x76
If I replace this in any of the above code with a variable, I get the same "expected an expression" error. I suspect the two problems I have are due to the same lack of understanding on my part. SO forgive me for combining the two in a single post.
So the questions are:
-Why am I getting this error?
-Is it possible to change individual items in my array, and if so how?
-Is it possible to use a variable in place of the MSBARO5X_ADDR, and if so how?
Many thanks!
Ultimately, the macro expands into a brace enclosed initializer. Such a thing is not an expression, so it cannot be used as the right hand side of plain assignment (assignment and initialization are different things). It will work as part of a larger initializer, but not the way you try to use it unmodified.
But all is not lost. The syntax of the initializer implies c99 support. So we can use a trick. Structure objects can be assigned to eachother. So we need only obtain an object from somewhere. We can use a compound literal in order to create said object:
transfers_1[0] = (nrf_twi_mngr_transfer_t)NRF_TWI_MNGR_WRITE(/*Your arguments*/);
If you define the value of a structure the moment you declare it, the compiler will infer the type of the structure from the declaration. So this here will compile:
struct coordinates {
int x;
int y;
};
struct coordinates origin = { 10, 20 }; // This is OK
But if you assign a value to a previously declared variable, the compiler cannot infer its type. This code won't compile:
struct coordinates origin;
origin = { 10, 20 }; // ERROR! The type of the rvalue is unknown!
The type is unknown, because two structures are not equivalent in C just because they have the same members. E.g. this is legal in C:
struct coordinates {
int x;
int y;
};
struct dayOfYear {
int day;
int month;
};
Now what would { 5, 8 } be? The coordinates (5/8) or the 5th of August? It could be both. All that he compiler knows is that it is a struct of type { int, int }. Yet this does not define a type in C. The following is possible in some languages but it's not possible in C:
struct dayOfYear date = { 2, 3 };
struct coordinates cords = date; // ERROR!
Despite the fact that both structures are of type { int, int }, for the compiler struct dayOfYear and struct coordinates are two completely distinct and unrelated data types.
If you want to declare a hardcoded struct value, you need to tell the compiler what kind of struct that is:
struct coordinates origin;
origin = (struct coordinates){ 10, 20 }; // This is OK
Your NRF_TWI_MNGR_TRANSFER defines a hardcoded struct but only when you use that in a definition the compiler knows the type. If you try to use it as an assignment, you need to cast to the correct type.
transfers_1[0] = (nrf_twi_mngr_transfer_t)NRF_TWI_MNGR_WRITE(MSBARO5X_0_ADDR , ®_addr[1], 1, NRF_TWI_MNGR_NO_STOP);
Which is not really a cast, even though it has the same syntax. In fact this is just telling the compiler how to interpret the following data.
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!
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];
}
}
// ...
}
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 !!
I have two structs I'm working with, and they are defined nearly identical. These are defined in header files that I cannot modify.
typedef struct
{
uint32_t property1;
uint32_t property2;
} CarV1;
typedef struct
{
uint32_t property1;
uint32_t property2;
/* V2 specific properties */
uint32_t property3;
uint32_t property4;
} CarV2;
In my code, I initialize the V2 struct at the top of my file, to cover all my bases:
static const carV2 my_car = {
.property1 = value,
.property2 = value,
/* V2 specific properties */
.property3 = value,
.property4 = value
};
Later, I want to retrieve the values I have initialized and copy them into the struct to be returned from a function via void pointer. I sometimes want V2 properties of the car, and sometimes V1. How can I memcpy safely without having duplicate definitions/initializations? I'm fairly new to C, and its my understanding that this is ugly and engineers to follow me in looking at this code will not approve. What's a clean way to do this?
int get_properties(void *returned_car){
int version = get_version();
switch (version){
case V1:
{
CarV1 *car = returned_car;
memcpy(car, &my_car, sizeof(CarV1)); // is this safe? What's a better way?
}
case V2:
{
CarV2 *car = returned_car;
memcpy(car, &my_car, sizeof(CarV2));
}
}
}
Yes, it's definitely possible to do what you're asking.
You can use a base struct member to implement inheritance, like this:
typedef struct
{
uint32_t property1;
uint32_t property2;
} CarV1;
typedef struct
{
CarV1 base;
/* V2 specific properties */
uint32_t property3;
uint32_t property4;
} CarV2;
In this case, you're eliminating the duplicate definitions. Of course, on a variable of type CarV2*, you can't reference the fields of the base directly - you'll have to do a small redirection, like this:
cv2p->base.property1 = 0;
To upcast to CarV1*, do this:
CarV1* cv1p = &(cv2p->base);
c1vp->property1 = 0;
You've written memcpy(&car, &my_car, sizeof(CarV1)). This looks like a mistake, because it's copying the data of the pointer variable (that is, the address of your struct, instead of the struct itself). Since car is already a pointer (CarV1*) and I'm assuming that so is my_car, you probably wanted to do this instead:
memcpy(car, my_car, sizeof(CarV1));
If my_car is CarV2* and car is CarV1* (or vice versa), then the above code is guaranteed to work by the C standard, because the first member of a struct is always at a zero offset and, therefore, the memory layout of those two for the first sizeof(CarV1) bytes will be identical.
The compiler is not allowed to align/pad that part differently (which I assume is what you meant about optimizing), because you've explicitly declared the first part of CarV2 to be a CarV1.
Since in your case you are stuck with identically defined structs that you can't change, you may find useful that the C standard defines a macro/special form called offsetof.
To be absolutely sure about your memory layouts, I'd advise that you put a series of checks during the initialization phase of your program that verifies whether the offsetof(struct CarV1, property1) is equal to offsetof(struct CarV2, property1) etc for all common properties:
void validateAlignment(void)
{
if (offsetof(CarV1, property1) != offsetof(CarV2, property1)) exit(-1);
if (offsetof(CarV1, property2) != offsetof(CarV2, property2)) exit(-1);
// and so on
}
This will stop the program for going ahead in case the compiler has done anything creative with the padding.
It also won't slow down your program's initialization because offsetof is actually calculated at compile time. So, with all the optimizations in place, the void validateAlignment(void) function should be optimized out completely (because a static analysis would show that the exit conditions are always false).
What you wrote will almost work, except that instead of memcpy(&car, ... you should just have memcpy (car, ..., but there is no reason to use memcpy in such a case. Rather, you should just copy each of the fields in a separate statement.
car->property1 = my_car.property1
(is my_car a pointer or not? it's impossible to tell from the code fragment)
For the second case, I think you can just assign the entire struct: *car = my_car
there is no perfect solution but one way is to use a union
typedef union car_union
{
CarV1 v1;
CarV2 v2;
} Car;
that way the size will not differ when you do a memcpy - if version v1 then v2 specific parts will not be initialized.
In C and Objective-C, this is fine in practice. (In theory, the compiler must see the declaration of a union containing both structs as members).
In C++ (and Objective-C++), the language very carefully describes when this is safe and when it isn't. For example, if you start with
typedef struct {
public:
...
then the compiler is free to re-arrange where struct members are. If the struct uses no C++ features then you are safe.
I'm trying to create structs with default values. I don't know how to accomplish this because every code that I see, is about initialising, and I would it for the natural way like...
struct stuff {
int stuff_a = 1;
int stuff_b = 2...
...and so on...
};
and looking about, I found this (C++) code:
struct a{ a() : i(0), j(0) {}; INT i; INT j;}
I never saw anything like this for C. Please, help me to understand it; I think that it is very nice!
UPDATE: Wait, I'm asking about C!!!! Why changed my question? If that is not possible in C just say... I don't know C++, I didn't know that was about C++...
If you want to set a struct object in one go and you have a C99 compiler, try this:
struct stuff {
int stuff_a;
int stuff_b;
// and so on...
};
struct stuff foo;
/* ... code ... */
foo = (struct stuff){.stuff_b = 42, .stuff_a = -1000};
Otherwise, with a C89 compiler, you have to set each member one by one:
foo.stuff_b = 42;
foo.stuff_a = -1000;
Running example # ideone : http://ideone.com/1QqCB
The original line
struct a{ a() : i(0), j(0) {} INT i; INT j;}
is a syntax error in C.
As you have probably learned from the other answers, in C you can't declare a structure and initialize it's members at the same time. These are different tasks and must be done separately.
There are a few options for initializing member variables of a struct. I'll show a couple of ways below. Right now, let's assume the following struct is defined in the beginning of the file:
struct stuff {
int stuff_a;
int stuff_b;
};
Then on your main() code, imagine that you want to declare a new variable of this type:
struct stuff custom_var;
This is the moment where you must initialize the structure. Seriously, I mean you really really must! Even if you don't want to assign specific values to them, you must at least initialize them to zero. This is mandatory because the OS doesn't guarantee that it will give you a clean memory space to run your application on. Therefore, always initialize your variables to some value (usually 0), including the other default types, such as char, int, float, double, etc...
One way to initialize our struct to zero is through memset():
memset(&custom_var, 0, sizeof(struct stuff));
Another is accessing each member individually:
custom_var.stuff_a = 0;
custom_var.stuff_b = 0;
A third option, which might confuse beginners is when they see the initialization of struct members being done at the moment of the declaration:
struct stuff custom_var = { 1, 2 };
The code above is equivalent to:
struct stuff custom_var;
custom_var.stuff_a = 1;
custom_var.stuff_b = 2;
... create structs with default values ...
That is impossible in C. A type cannot have default values. Objects of any type cannot have a default value other than 0, though they can be initialized to whatever is wanted.
The definition of a struct is a definition of a type, not of an object.
What you asking is about the same thing as a way to have ints default to, say, 42.
/* WRONG CODE -- THIS DOES NOT WORK */
typedef int int42 = 42;
int42 a;
printf("%d\n", a); /* print 42 */
Or, adapting to your example
/* WRONG CODE -- THIS DOES NOT WORK */
struct stuff {
int42 stuff_a;
int65536 stuff_b;
}
struct stuff a;
printf("%d\n", a.stuff_b); /* print 65536 */
Update: This answer assumes we 're talking about C++ because the code posted in the answer is not legal C.
struct a {
a() : i(0), j(0) {} // constructor with initialization list
int i;
int j;
}
The line marked with the comment is simply the constructor for instances of struct a (reminder: structs are just like classes, except that the default member visibility is public instead of private).
The part after the : is called an initialization list: it allows you to initialize the members of the struct with values (either constants or passed as constructor parameters). Initialization of members in this list happens before the body of the constructor is entered. It is preferable to initialize members of classes and structs this way, if at all possible.
See also C++: Constructor versus initializer list in struct/class.
in C (pre C99) the following also works:
#include <stdio.h>
typedef struct
{
int a;
int b;
int c;
} HELLO;
int main()
{
HELLO a = {1,2,3};
printf("here: %d %d %d\n",a.a,a.b,a.c);
exit(1);
}
See codepad
I'm not sure quite sure what your problem is. The standard way of initialising structures in c is like this:
struct a_struct my_struct = {1, 2};
Or the more recent and safer:
struct a_struct my_struct = {.i1 = 1, .i2 = 2};
If there is more than one instance of a structure, or it needs to be re-initialised, it is useful to define a constant structure with default values then assign that.
typedef struct a_struct {
int i1;
int i2;
} sa;
static const sa default_sa = {.i1 = 1, .i2 = 2};
static sa sa1 = default_sa;
static sa sa2 = default_sa;
// obviously you can do it dynamically as well
void use_temp_sa(void)
{
sa temp_sa = default_sa;
temp_sa.i2 = 3;
do_something_with(&temp_sa);
}
// And re-initialise
void reset_sa(sa *my_sa)
{
*my_sa = default_sa;
}
Type initializer is not possible in C.
A value must be stored in the memory.
A type does not occupy memory, what occupies memory is a variable of that type.
struct stuff; is a type; it does not occupy memory
struct stuff aStuff; is a variable of that type; aStuff occupies memory
Because a type does not occupy memory, it is not possible to save values into a type.
If there is syntactic sugar to support store/initialize values into a type then there must be additional code that is inserted to assign values to every instant variables of that type (e.g: in constructor in C++). This will result in a less efficient C if this feature is available.
How often do you need to retain this default values? I think it is unlikely. You can create a function to initialize variable with the default values or just initialize every fields with the values you want. So type initializer is not fundamental thing. C is about simplicity.
Can't initialize values within a structure definition.
I'd suggest:
typedef struct {
int stuff_a;
int stuff_b;
} stuff ;
int stuffInit(int a, int b, stuff *this){
this->stuff_a = a;
this->stuff_b = b;
return 0; /*or an error code, or sometimes '*this', per taste.*/
}
int main(void){
stuff myStuff;
stuffInit(1, 2, &myStuff);
/* dynamic is more commonly seen */
stuff *dynamicStuff;
dynamicStuff = malloc(sizeof(stuff)); /* 'new' stuff */
stuffInit(0, 0, dynamicStuff);
free(dynamicStuff); /* 'delete' stuff */
return 0;
}
Before the days of Object Oriented Programming (C++), we were taught "Abstract Data Types".
The discipline said 'never access your data structures directly, always create a function for it' But this was only enforced by the programmer, instructor, or senior developer, not the language.
Eventually, the structure definition(s) and corresponding functions end up in their own file & header, linked in later, further encapsulating the design.
But those days are gone and replaced with 'Class' and 'Constructor' OOP terminology.
"It's all the same, only the names have changed" - Bon Jovi.
I have a data structure like this:
struct foo {
int id;
int route;
int backup_route;
int current_route;
}
and a function called update() that is used to request changes in it.
update(42, dont_care, dont_care, new_route);
this is really long and if I add something to the structure I have to add a 'dont_care' to EVERY call to update( ... ).
I am thinking about passing it a struct instead but filling in the struct with 'dont_care' beforehand is even more tedious than just spelling it out in the function call. Can I create the struct somewhere with default values of dont care and just set the fields I care about after I declare it as a local variable?
struct foo bar = { .id = 42, .current_route = new_route };
update(&bar);
What is the most elegant way to pass just the information I wish to express to the update function?
and I want everything else to default to -1 (the secret code for 'dont care')
While macros and/or functions (as already suggested) will work (and might have other positive effects (i.e. debug hooks)), they are more complex than needed. The simplest and possibly most elegant solution is to just define a constant that you use for variable initialisation:
const struct foo FOO_DONT_CARE = { // or maybe FOO_DEFAULT or something
dont_care, dont_care, dont_care, dont_care
};
...
struct foo bar = FOO_DONT_CARE;
bar.id = 42;
bar.current_route = new_route;
update(&bar);
This code has virtually no mental overhead of understanding the indirection, and it is very clear which fields in bar you set explicitly while (safely) ignoring those you do not set.
You can change your secret special value to 0, and exploit C's default structure-member semantics
struct foo bar = { .id = 42, .current_route = new_route };
update(&bar);
will then pass 0 as members of bar unspecified in the initializer.
Or you can create a macro that will do the default initialization for you:
#define FOO_INIT(...) { .id = -1, .current_route = -1, .quux = -1, ## __VA_ARGS__ }
struct foo bar = FOO_INIT( .id = 42, .current_route = new_route );
update(&bar);
<stdarg.h> allows you to define variadic functions (which accept an indefinite number of arguments, like printf()). I would define a function which took an arbitrary number of pairs of arguments, one which specifies the property to be updated, and one which specifies the value. Use an enum or a string to specify the name of the property.
Perhaps consider using a preprocessor macro definition instead:
#define UPDATE_ID(instance, id) ({ (instance)->id= (id); })
#define UPDATE_ROUTE(instance, route) ({ (instance)->route = (route); })
#define UPDATE_BACKUP_ROUTE(instance, route) ({ (instance)->backup_route = (route); })
#define UPDATE_CURRENT_ROUTE(instance, route) ({ (instance)->current_route = (route); })
If your instance of (struct foo) is global, then you don't need the parameter for that of course. But I'm assuming you probably have more than one instance. Using the ({ ... }) block is a GNU-ism that that applies to GCC; it is a nice (safe) way to keep lines together as a block. If you later need to add more to the macros, such as range validation checking, you won't have to worry about breaking things like if/else statements and so forth.
This is what I would do, based upon the requirements you indicated. Situations like this are one of the reasons that I started using python a lot; handling default parameters and such becomes a lot simpler than it ever is with C. (I guess that's a python plug, sorry ;-)
One pattern gobject uses is a variadic function, and enumerated values for each property. The interface looks something like:
update (ID, 1,
BACKUP_ROUTE, 4,
-1); /* -1 terminates the parameter list */
Writing a varargs function is easy -- see http://www.eskimo.com/~scs/cclass/int/sx11b.html. Just match up key -> value pairs and set the appropriate structure attributes.
Since it looks like that you only need this structure for the update() function, don't use a structure for this at all, it will only obfuscate your intention behind that construct. You should maybe rethink why you are changing and updating those fields and define separate functions or macros for this "little" changes.
e.g.
#define set_current_route(id, route) update(id, dont_care, dont_care, route)
#define set_route(id, route) update(id, dont_care, route, dont_care)
#define set_backup_route(id, route) update(id, route, dont_care, dont_care)
Or even better write a function for every change case. As you already noticed you don't change every property at the same time, so make it possible to change only one property at a time. This doesn't only improve the readability, but also helps you handling the different cases, e.g. you don't have to check for all the "dont_care" because you know that only the current route is changed.
How about something like:
struct foo bar;
update(init_id(42, init_dont_care(&bar)));
with:
struct foo* init_dont_care(struct foo* bar) {
bar->id = dont_care;
bar->route = dont_care;
bar->backup_route = dont_care;
bar->current_route = dont_care;
return bar;
}
and:
struct foo* init_id(int id, struct foo* bar) {
bar->id = id;
return bar;
}
and correspondingly:
struct foo* init_route(int route, struct foo* bar);
struct foo* init_backup_route(int backup_route, struct foo* bar);
struct foo* init_current_route(int current_route, struct foo* bar);
In C++, a similar pattern has a name which I don't remember just now.
EDIT: It's called the Named Parameter Idiom.
I'm rusty with structs, so I'm probably missing a few keywords here. But why not start with a global structure with the defaults initialized, copy it to your local variable, then modify it?
An initializer like:
void init_struct( structType * s )
{
memcopy(s,&defaultValues,sizeof(structType));
}
Then when you want to use it:
structType foo;
init_struct( &foo ); // get defaults
foo.fieldICareAbout = 1; // modify fields
update( &foo ); // pass to function
You could address the problem with an X-Macro
You would change your struct definition into:
#define LIST_OF_foo_MEMBERS \
X(int,id) \
X(int,route) \
X(int,backup_route) \
X(int,current_route)
#define X(type,name) type name;
struct foo {
LIST_OF_foo_MEMBERS
};
#undef X
And then you would be able to easily define a flexible function that sets all fields to dont_care.
#define X(type,name) in->name = dont_care;
void setFooToDontCare(struct foo* in) {
LIST_OF_foo_MEMBERS
}
#undef X
Following the discussion here, one could also define a default value in this way:
#define X(name) dont_care,
const struct foo foo_DONT_CARE = { LIST_OF_STRUCT_MEMBERS_foo };
#undef X
Which translates into:
const struct foo foo_DONT_CARE = {dont_care, dont_care, dont_care, dont_care,};
And use it as in hlovdal answer, with the advantage that here maintenance is easier, i.e. changing the number of struct members will automatically update foo_DONT_CARE. Note that the last "spurious" comma is acceptable.
I first learned the concept of X-Macros when I had to address this problem.
It is extremely flexible to new fields being added to the struct. If you have different data types, you could define different dont_care values depending on the data type: from here, you could take inspiration from the function used to print the values in the second example.
If you are ok with an all int struct, then you could omit the data type from LIST_OF_foo_MEMBERS and simply change the X function of the struct definition into #define X(name) int name;
The most elegant way would be to update the struct fields directly, without having to use the update() function - but maybe there are good reasons for using it that don't come across in the question.
struct foo* bar = get_foo_ptr();
foo_ref.id = 42;
foo_ref.current_route = new_route;
Or you can, like Pukku suggested, create separate access functions for each field of the struct.
Otherwise the best solution I can think of is treating a value of '0' in a struct field as a 'do not update' flag - so you just create a funciton to return a zeroed out struct, and then use this to update.
struct foo empty_foo(void)
{
struct foo bar;
bzero(&bar, sizeof (struct bar));
return bar;
}
struct foo bar = empty_foo();
bar.id=42;
bar.current_route = new_route;
update(&bar);
However, this might not be very feasible, if 0 is a valid value for fields in the struct.