Valores predeterminados en una estructura C

Tengo una estructura de datos como esta:

     struct foo {
         int id;
         ruta int;
         int backup_route;
         int current_route;
     }

y una función llamada update () que se usa para solicitar cambios en ella.

   actualización (42, dont_care, dont_care, new_route);

esto es realmente largo y si agrego algo a la estructura, tengo que agregar un ‘dont_care’ a TODAS las llamadas para actualizar (…).

Estoy pensando en pasarle una estructura, pero completar la estructura con ‘dont_care’ de antemano es aún más tedioso que solo deletrearlo en la llamada a la función. ¿Puedo crear la estructura en algún lugar con los valores predeterminados de no me importa y simplemente establecer los campos que me preocupan después de declararlo como una variable local?

     struct foo bar = {.id = 42, .current_route = new_route};
     actualización (y barra);

¿Cuál es la manera más elegante de pasar solo la información que deseo express a la función de actualización?

y quiero que todo lo demás sea predeterminado a -1 (el código secreto para “no me importa”)

Mientras que las macros y / o funciones (como ya se sugirió) funcionarán (y podrían tener otros efectos positivos (es decir, enlaces de depuración)), son más complejas de lo necesario. La solución más simple y posiblemente más elegante es simplemente definir una constante que use para la inicialización variable:

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

Este código prácticamente no tiene una carga mental para comprender el direccionamiento indirecto, y está muy claro qué campos de la bar estableces explícitamente (ignorando de forma segura los que no configuraste).

Puede cambiar su valor especial secreto a 0 y explotar la semántica de miembro de estructura predeterminada de C

 struct foo bar = { .id = 42, .current_route = new_route }; update(&bar); 

pasará 0 como miembros de la barra no especificada en el inicializador.

O puede crear una macro que hará la inicialización predeterminada para usted:

 #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); 

permite definir funciones variadas (que aceptan un número indefinido de argumentos, como printf() ). Definiría una función que tomó un número arbitrario de pares de argumentos, uno que especifica la propiedad que se actualizará y otro que especifica el valor. Use una enum o una cadena para especificar el nombre de la propiedad.

Quizás considere utilizar una definición de macro de preprocesador en su lugar:

 #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); }) 

Si su instancia de (struct foo) es global, entonces no necesita el parámetro para eso, por supuesto. Pero supongo que probablemente tengas más de una instancia. Usar el bloque ({…}) es un GNU-ismo que se aplica a GCC; es una forma agradable (segura) de mantener las líneas juntas como un bloque. Si más adelante necesita agregar más a las macros, como la verificación de validación de rango, no tendrá que preocuparse por las cosas como declaraciones if / else, etc.

Esto es lo que haría, según los requisitos que indicó. Situaciones como esta son una de las razones por las que empecé a usar Python mucho; manejar los parámetros por defecto y eso se vuelve mucho más simple que nunca con C. (Supongo que es un complemento de Python, lo siento 😉

Un patrón que el usuario usa es una función variada y valores enumerados para cada propiedad. La interfaz se ve algo así como:

 update (ID, 1, BACKUP_ROUTE, 4, -1); /* -1 terminates the parameter list */ 

Escribir una función varargs es fácil – ver http://www.eskimo.com/~scs/cclass/int/sx11b.html . Simplemente haga coincidir la clave -> pares de valores y establezca los atributos de estructura apropiados.

Como parece que solo necesita esta estructura para la función update() , no use una estructura para esto, solo ofuscará su intención detrás de esa construcción. Debería replantearse por qué está cambiando y actualizando esos campos y definir funciones o macros separadas para estos “pequeños” cambios.

p.ej

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

O incluso mejor escribir una función para cada caso de cambio. Como ya ha notado, no cambia todas las propiedades al mismo tiempo, por lo que es posible cambiar solo una propiedad a la vez. Esto no solo mejora la legibilidad, sino que también te ayuda a manejar los diferentes casos, por ejemplo, no tienes que verificar todo el “dont_care” porque sabes que solo se cambia la ruta actual.

¿Qué tal algo así como:

 struct foo bar; update(init_id(42, init_dont_care(&bar))); 

con:

 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; } 

y:

 struct foo* init_id(int id, struct foo* bar) { bar->id = id; return bar; } 

y correspondientemente:

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

En C ++, un patrón similar tiene un nombre que no recuerdo en este momento.

EDITAR : Se llama la expresión idiomática de parámetros nombrados .

Estoy oxidado con las estructuras, así que probablemente me faltan algunas palabras clave aquí. Pero, ¿por qué no comenzar con una estructura global con los valores predeterminados inicializados, copiarla en su variable local y luego modificarla?

Un inicializador como:

 void init_struct( structType * s ) { memcopy(s,&defaultValues,sizeof(structType)); } 

Luego, cuando quieras usarlo:

 structType foo; init_struct( &foo ); // get defaults foo.fieldICareAbout = 1; // modify fields update( &foo ); // pass to function 

Podría abordar el problema con un X-Macro

Usted cambiaría su definición de estructura en:

 #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 

Y luego podrá definir fácilmente una función flexible que establece todos los campos en dont_care .

 #define X(type,name) in->name = dont_care; void setFooToDontCare(struct foo* in) { LIST_OF_foo_MEMBERS } #undef X 

Luego de la discusión aquí , también se podría definir un valor predeterminado de esta manera:

 #define X(name) dont_care, const struct foo foo_DONT_CARE = { LIST_OF_STRUCT_MEMBERS_foo }; #undef X 

Lo que se traduce en:

 const struct foo foo_DONT_CARE = {dont_care, dont_care, dont_care, dont_care,}; 

Y úsalo como en la respuesta hlovdal , con la ventaja de que aquí el mantenimiento es más fácil, es decir, al cambiar el número de miembros de la estructura se actualizará automáticamente foo_DONT_CARE . Tenga en cuenta que la última coma “espuria” es aceptable .

Primero aprendí el concepto de X-Macros cuando tuve que abordar este problema .

Es extremadamente flexible para agregar nuevos campos a la estructura. Si tiene diferentes tipos de datos, podría definir diferentes valores dont_care según el tipo de datos: desde aquí , podría inspirarse en la función utilizada para imprimir los valores en el segundo ejemplo.

Si está bien con una estructura all int , puede omitir el tipo de datos de LIST_OF_foo_MEMBERS y simplemente cambiar la función X de la definición de estructura a #define X(name) int name;

La forma más elegante sería actualizar los campos de estructura directamente, sin tener que usar la función de update() , pero tal vez haya buenas razones para usarlo que no aparecen en la pregunta.

 struct foo* bar = get_foo_ptr(); foo_ref.id = 42; foo_ref.current_route = new_route; 

O puede, como sugirió Pukku, crear funciones de acceso separadas para cada campo de la estructura.

De lo contrario, la mejor solución que puedo pensar es tratar un valor de ‘0’ en un campo de estructura como un indicador de ‘no actualizar’, por lo que solo crea un funciton para devolver una estructura a cero y luego lo utiliza para actualizar.

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

Sin embargo, esto puede no ser muy factible, si 0 es un valor válido para los campos en la estructura.