Valores predeterminados en una estructura C

7 minutos de lectura

avatar de usuario
Arturo Ulfeldt

Tengo una estructura de datos como esta:

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

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

update(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 CADA llamada para actualizar (…).

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

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

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

y quiero que todo lo demás tenga el valor predeterminado -1 (el código secreto para ‘no me importa’)

avatar de usuario
hlovdal

Si bien las macros y/o funciones (como ya se sugirió) funcionarán (y podrían tener otros efectos positivos (es decir, ganchos 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 de variables:

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 sobrecarga mental para comprender la indirección, y es muy claro qué campos en bar establece explícitamente mientras (de forma segura) ignora aquellos que no establece.

  • Otra ventaja de este enfoque es que no depende de las características de un C99 para funcionar.

    – D. Shawley

    15 de abril de 2009 a las 18:28

  • Cuando cambié a estas 500 líneas “se cayeron” del proyecto. ¡Ojalá pudiera votar dos veces por este!

    – Arturo Ulfeldt

    12 de junio de 2009 a las 17:53

  • PTHREAD_MUTEX_INITIALIZER usa esto también.

    – yingted

    2 de marzo de 2012 a las 4:50

  • He agregado una respuesta a continuación confiando en X-Macros por ser flexible en la cantidad de elementos presentes en la estructura.

    – Antonio

    12 de abril de 2017 a las 16:02

avatar de usuario
jpalecek

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

luego pasará 0 como miembros de bar no especificados en el inicializador.

O puede crear una macro que hará la inicialización predeterminada por 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);

  • ¿Funciona FOO_INIT? Creo que el compilador debería quejarse si inicializa el mismo miembro dos veces.

    –Jonathan Leffler

    15 de abril de 2009 a las 15:17

  • Lo probé con gcc y no se quejó. Además, no he encontrado nada en contra en el estándar, de hecho, hay un ejemplo en el que se menciona específicamente la sobrescritura.

    – jpalecek

    15 de abril de 2009 a las 19:40

  • C99: open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf: 6.7.8.19: La inicialización debe ocurrir en el orden de la lista de inicializadores, cada inicializador provisto para un subobjeto particular anula cualquier inicializador listado previamente para el mismo subobjeto;

    – altendky

    14 de diciembre de 2012 a las 18:08


  • Si esto es cierto, ¿no podría definir DEFAULT_FOO, que tiene todos los inicializadores, y luego hacer struct foo bar = { DEFAULT_FOO, .id=10 }; ?

    – Juan

    23 de diciembre de 2013 a las 18:56

avatar de usuario
Matt J.

<stdarg.h> le permite definir funciones variádicas (que aceptan un número indefinido de argumentos, como printf()). Definiría una función que tomara un número arbitrario de pares de argumentos, una que especifique la propiedad que se actualizará y otra que especifique el valor. Usa un enum o una cadena para especificar el nombre de la propiedad.

  • Hola @Matt, cómo mapear enum variable a la struct ¿campo? ¿Codificarlo? Entonces esta función sólo será aplicable a determinados struct.

    – señorita sprite

    15 de diciembre de 2014 a las 8:20

Quizás considere usar 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 luego necesita agregar más a las macros, como la verificación de validación de rango, no tendrá que preocuparse por romper cosas como declaraciones if/else, etc.

Esto es lo que yo haría, en base a los requisitos que usted indicó. Situaciones como esta son una de las razones por las que comencé a usar mucho Python; manejar parámetros predeterminados 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 utiliza gobject es una función variable y valores enumerados para cada propiedad. La interfaz se ve algo como:

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

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

avatar de usuario
quinmars

Como parece que solo necesita esta estructura para el update() función, no use una estructura para esto en absoluto, solo ofuscará su intención detrás de esa construcción. Tal vez debería repensar 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, escriba una función para cada caso de cambio. Como ya notó, no cambia todas las propiedades al mismo tiempo, así que haga posible cambiar solo una propiedad a la vez. Esto no solo mejora la legibilidad, sino que también lo ayuda a manejar los diferentes casos, por ejemplo, no tiene que verificar todos los “no importa” porque sabe que solo se cambia la ruta actual.

avatar de usuario
Paddu

¿Qué tal algo 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 ahora mismo no recuerdo.

EDITAR: se llama el Modismo de parámetro con nombre.

  • Creo que se llama constructor.

    – Desconocido

    14 de abril de 2009 a las 20:23

  • No, quise decir lo que puedes hacer: actualizar (bar.init_id (42). init_route (5). init_backup_route (66));

    – Reunión

    14 de abril de 2009 a las 20:27

  • Parece que se llama “inicialización encadenada al estilo de Smalltalk”, al menos aquí: cct.lsu.edu/~rguidry/ecl31docs/api/org/eclipse/ui/internal/…

    – Reunión

    14 de abril de 2009 a las 21:01

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad