Estoy escribiendo un gran programa de C para uso integrado. Cada módulo en este programa tiene una función init() (como un constructor) para configurar sus variables estáticas.¿Cómo puedo verificar que se hayan llamado todas mis funciones init?
El problema es que tengo que recordar llamar a todas estas funciones de inicio desde main()
. También debo recordar devolverlos si los he comentado por alguna razón.
¿Hay algo inteligente que hacer para asegurarme de que se llamen todas estas funciones? Algo parecido a poner una macro en cada función de inicio que, cuando llamas a una función check_inited()
más tarde, envía una advertencia a STDOUT si no se llaman todas las funciones.
Podría incrementar un contador, pero tendría que mantener el número correcto de funciones init en alguna parte y también es propenso a error.
¿Pensamientos?
La siguiente es la solución que se me decidí por, con la colaboración de varias personas en este hilo
Mi objetivo es asegurarse de que todas mis funciones init en realidad están siendo llamados. Quiero hacer esto sin mantener listas o recuentos de módulos en varios archivos. No puedo llamarlos automáticamente al como sugirió Nick D porque deben llamarse en un orden determinado.
Para lograr esto, una macro incluida en cada módulo usa el atributo gcc constructor
en agregue el nombre de la función init a una lista global.
Otra macro incluida en el cuerpo de la función init actualiza la lista global para hacer que un tenga en cuenta que realmente se llamó a la función.
Finalmente, se llama a una función de verificación en main()
después de que se completen todas las operaciones.
Notas:
he elegido para copiar las cadenas en una matriz. Esto no es estrictamente necesario porque los nombres de función pasados siempre serán cadenas estáticas en el uso normal. Si la memoria era corta , podría simplemente almacenar un puntero a la cadena que se pasó.
Mi biblioteca reutilizable de funciones de utilidad se llama "nx_lib". Por lo tanto, todas las designaciones 'nxl'.
Este no es el código más eficiente del mundo, pero solo se llama tiempo de arranque, por lo que no me importa.
Hay dos líneas de código que deben agregarse a cada módulo. Si se omite cualquiera de los dos, , la función de verificación le avisará.
es posible que pueda hacer que la función del constructor sea estática, lo que evitaría la necesidad de darle un nombre único en todo el proyecto.
este código solo se probó ligeramente y es muy tarde, por favor revise cuidadosamente antes de confiar en él.
Gracias a:
pierr que me introdujo en el atributo constructor
.
Nick D por demostrar el truco del preprocesador ## y por proporcionarme el marco.
tod frye para un enfoque inteligente basado en un vinculador que funcionará con muchos compiladores.
Todos los demás para ayudar y compartir cositas útiles.
nx_lib_public.h
Este es el fragmento relevante de mi archivo de cabecera biblioteca
#define NX_FUNC_RUN_CHECK_NAME_SIZE 20
typedef struct _nxl_function_element{
char func[NX_FUNC_RUN_CHECK_NAME_SIZE];
BOOL called;
} nxl_function_element;
void nxl_func_run_check_add(char *func_name);
BOOL nxl_func_run_check(void);
void nxl_func_run_check_hit(char *func_name);
#define NXL_FUNC_RUN_CHECK_ADD(function_name) \
void cons_ ## function_name() __attribute__((constructor)); \
void cons_ ## function_name() { nxl_func_run_check_add(#function_name); }
nxl_func_run_check.c
Este es el código que se llama libary añadir los nombres de funciones y comprobar más tarde.
#define MAX_CHECKED_FUNCTIONS 100
static nxl_function_element m_functions[MAX_CHECKED_FUNCTIONS];
static int m_func_cnt = 0;
// call automatically before main runs to register a function name.
void nxl_func_run_check_add(char *func_name)
{
// fail and complain if no more room.
if (m_func_cnt >= MAX_CHECKED_FUNCTIONS) {
print ("nxl_func_run_check_add failed, out of space\r\n");
return;
}
strncpy (m_functions[m_func_cnt].func, func_name,
NX_FUNC_RUN_CHECK_NAME_SIZE);
m_functions[m_func_cnt].func[NX_FUNC_RUN_CHECK_NAME_SIZE-1] = 0;
m_functions[m_func_cnt++].called = FALSE;
}
// call from inside the init function
void nxl_func_run_check_hit(char *func_name)
{
int i;
for (i=0; i< m_func_cnt; i++) {
if (! strncmp(m_functions[i].func, func_name,
NX_FUNC_RUN_CHECK_NAME_SIZE)) {
m_functions[i].called = TRUE;
return;
}
}
print("nxl_func_run_check_hit(): error, unregistered function was hit\r\n");
}
// checks that all registered functions were called
BOOL nxl_func_run_check(void) {
int i;
BOOL success=TRUE;
for (i=0; i< m_func_cnt; i++) {
if (m_functions[i].called == FALSE) {
success = FALSE;
xil_printf("nxl_func_run_check error: %s() not called\r\n",
m_functions[i].func);
}
}
return success;
}
solo.c
Este es un ejemplo de un módulo que necesita la inicialización
#include "nx_lib_public.h"
NXL_FUNC_RUN_CHECK_ADD(solo_init)
void solo_init(void)
{
nxl_func_run_check_hit((char *) __func__);
/* do module initialization here */
}
.. y si olvida poner la macro en una función init particular? Entonces vuelves al punto uno. –
Bueno, sí, pero al menos eso es algo que puedo hacer mientras escribo la función, es más fácil recordar hacer eso que sincronizar algo en otro archivo. – NXT
¿No sería mejor escribir el error en stderr? –