如何检查是否已调用所有init函数?

NXT*_*NXT 5 c embedded

我正在编写一个用于嵌入式使用的大型C程序.该程序中的每个模块都有一个init()函数(如构造函数)来设置其静态变量.

问题是我必须记住从中调用所有这些init函数main().如果我出于某种原因对它们进行了评论,我也必须记得把它们放回去.

我有什么聪明才能确保所有这些功能都被调用?在每个init函数中放置一个宏的东西,当你check_inited()稍后调用一个函数时,如果没有调用所有的函数,它会向STDOUT发送一个警告.

我可以增加一个计数器,但我必须在某处保持正确数量的init函数,这也容易出错.

思考?

以下是我决定使用的解决方案,该线程中有来自几个人的输入

我的目标是确保实际调用所有的init函数.我想在不维护多个文件的模块列表或计数的情况下执行此操作.我不能像Nick D建议的那样自动调用它们,因为它们需要按特定顺序调用.

为此,每个模块中包含的宏使用gcc constructor属性将init函数名称添加到全局列表中.

init函数体中包含的另一个宏更新全局列表,以记录该函数实际被调用.

最后,在main()完成所有操作后调用check函数.

笔记:

  1. 我选择将字符串复制到数组中.这不是绝对必要的,因为传递的函数名称在正常使用中始终是静态字符串.如果内存很短,你可以只存储一个指向传入的字符串的指针.

  2. 我可重用的实用函数库叫做"nx_lib".因此所有'nxl'指定.

  3. 这不是世界上最有效的代码,但它只被称为启动时间,因此对我来说无关紧要.

  4. 需要向每个模块添加两行代码.如果省略任何一个,检查功能会通知您.

  5. 您可能能够将构造函数设置为静态,这样就无需为其提供在整个项目中唯一的名称.

  6. 这个代码只是经过了轻微的测试而且确实很晚,所以在信任之前请仔细检查.

谢谢至:

皮尔尔向我介绍了这个constructor属性.

Nick D用于演示##预处理器技巧并给我框架.

tod frye用于基于链接器的聪明方法,可以与许多编译器一起使用.

其他人帮助和分享有用的花絮.

nx_lib_public.h

这是我的库头文件的相关片段

#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); }
Run Code Online (Sandbox Code Playgroud)

nxl_func_run_check.c

这是用于添加函数名称并稍后检查它们的库代码.

#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;
}
Run Code Online (Sandbox Code Playgroud)

solo.c

这是需要初始化的模块的示例

#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 */
}
Run Code Online (Sandbox Code Playgroud)

Nic*_*kis 1

我不知道下面的内容看起来有多难看,但我还是发布了:-)

(基本思想是注册函数指针,就像atexit函数所做的那样。
当然atexit实现是不同的)

在主模块中我们可以有这样的东西:

typedef int (*function_t)(void);

static function_t  vfunctions[100]; // we can store max 100 function pointers
static int         vcnt = 0; // count the registered function pointers

int add2init(function_t f)
{
  // todo: error checks
  vfunctions[vcnt++] = f;
  return 0;
}
...

int main(void) {
 ...
 // iterate vfunctions[] and call the functions
 ...
}
Run Code Online (Sandbox Code Playgroud)

...以及在其他一些模块中:

typedef int (*function_t)(void);
extern int add2init(function_t f);
#define M_add2init(function_name)  static int int_ ## function_name = add2init(function_name)

int foo(void)
{
   printf("foo\n");
   return 0;
}
M_add2init(foo); // <--- register foo function
Run Code Online (Sandbox Code Playgroud)