Me pregunto si es posible usar uniones como argumentos para una función:
Digamos que tengo dos estructuras:
struct complex_attribute{ struct generic_attribute *sub_attributes[20]; }; struct generic_attribute{ int current_value; };Y una unión de estos dos:
union union_attribute{ struct complex_attribute *complex; struct generic_attribute *generic; };Quiero crear una función que admita un atributo_complejo o un atributo_genérico:
struct tagged_attribute* prepare_tagged_attribute(int code, union union_attribute *attribute)Sin embargo, cuando hago una llamada a esta función
prepare_tagged_attribute(2, pointer_to_complex_structure);me sale este error:
passing argument 2 of 'prepare_tagged_attribute' from incompatible pointer typeEntonces, supongo que el puntero a una estructura compleja no es necesariamente un puntero de tipo union (lo cual tiene sentido)... pero entonces, ¿es posible usar uniones de esta manera?
Si está usando GCC y está dispuesto a usar sus extensiones de idioma, puede lograr lo que está tratando de hacer con un transparent_union .
De https://gcc.gnu.org/onlinedocs/gcc-3.1/gcc/Type-Attributes.html :
Las uniones transparentes están diseñadas para funciones de biblioteca que tienen múltiples interfaces por razones de compatibilidad. Por ejemplo, suponga que la función de espera debe aceptar un valor de tipo int * para cumplir con Posix, o un valor de tipo union wait * para cumplir con la interfaz 4.1BSD. Si el parámetro de espera fuera nulo *, esperar aceptaría ambos tipos de argumentos, pero también aceptaría cualquier otro tipo de puntero y esto haría que la verificación del tipo de argumento fuera menos útil. En su lugar, podría definir la interfaz de la siguiente manera:
typedef union { int *__ip; union wait *__up; } wait_status_ptr_t __attribute__ ((__transparent_union__)); pid_t wait (wait_status_ptr_t); En su ejemplo, declararía union_attribute así:
typedef union { struct complex_attribute *complex; struct generic_attribute *generic; } union_attribute __attribute__ ((__transparent_union__)); Y luego puede llamar a prepare_tagged_attribute exactamente como lo propuso.
struct tagged_attribute* prepare_tagged_attribute(int code, union_attribute attribute) { attribute.generic_attribute->current_value = 1; // eg return NULL; // eg } prepare_tagged_attribute(code, pointer_to_complex_structure);Dado que no hay polimorfismo ni sobrecarga en C, no estoy seguro de que haya una solución perfecta para esto.
Su solución funcionará, siempre que envuelva su puntero en un union_attribute :
union union_attribute argument; argument.complex = pointer_to_complex_structure; prepare_tagged_attribute(2, &argument);Es un poco molesto tener que hacer este ajuste, por lo que una opción sería introducir una nueva función para que lo haga por ti:
union union_attribute wrap_complex(struct complex_attribute* attr) { union union_attribute result; result.complex = attr; return result; } Luego, reescriba su función prepare_tagged_attribute para tomar una union por valor en lugar de por puntero:
struct tagged_attribute* prepare_tagged_attribute(int code, union union_attribute attribute)Entonces, puedes decir algo como
prepare_tagged_attribute(2, wrap_complex(pointer_to_complex_structure));No es perfecto, seguro, pero funciona y se verifica.
¡Espero que esto ayude!
Como declaró la función para tomar un argumento de unión, en realidad tiene que pasar un argumento de unión , no un puntero.
Nota al margen: si usa gcc, eche un vistazo al atributo transparent_union . Eso podría ser interesante para usted, ya que en realidad permite el uso anterior. Tenga en cuenta, sin embargo, que todavía tiene que señalar qué tipo de argumento está pasando (tanto como para la forma estándar.
Una forma posiblemente mejor sería agrupar la información de tipo y valor en una sola estructura como:
struct variable_argument { enum { COMPLEX_ARG, GENERIC_ARG } type; union union_attribute{ struct complex_attribute *complex; struct generic_attribute *generic; }; };y pasar un puntero a tal estructura a la función. Tenga en cuenta que utiliza un campo de estructura anónimo para la unión (desde C99). De esta manera puedes referir los campos como:
struct variable_argument s; ... s.complex = ... Sin embargo, nunca olvide configurar s.type apropiadamente si cambia el tipo de unión.
Esta estructura en realidad relaciona sus punteros con una de las otras estructuras. Sin embargo, puede tenerlos directamente en la unión en lugar de usar punteros. Eso simplifica la asignación/liberación de memoria.