C 如何检查我的所有init函数是否都已被调用?
我正在编写一个用于嵌入式的大型C程序。该程序中的每个模块都有一个init()函数(类似于构造函数)来设置其静态变量 问题是我必须记住从C 如何检查我的所有init函数是否都已被调用?,c,embedded,C,Embedded,我正在编写一个用于嵌入式的大型C程序。该程序中的每个模块都有一个init()函数(类似于构造函数)来设置其静态变量 问题是我必须记住从main()调用所有这些init函数。如果我出于某种原因对它们进行了评论,我还必须记得将它们放回原处 有没有什么聪明的方法可以确保所有这些函数都被调用?在每个init函数中放入一个宏,当您稍后调用check_init()函数时,如果没有调用所有函数,则会向STDOUT发送警告 我可以增加一个计数器,但我必须在某个地方保持正确数量的init函数,这也容易出错 想法
main()
调用所有这些init函数。如果我出于某种原因对它们进行了评论,我还必须记得将它们放回原处
有没有什么聪明的方法可以确保所有这些函数都被调用?在每个init函数中放入一个宏,当您稍后调用check_init()
函数时,如果没有调用所有函数,则会向STDOUT发送警告
我可以增加一个计数器,但我必须在某个地方保持正确数量的init函数,这也容易出错
想法
以下是我决定的解决方案,该线程中有几个人提供了意见
我的目标是确保所有init函数都被实际调用。我想做什么
这不需要维护多个文件中的模块列表或计数。我不能打电话
正如Nick D所建议的那样自动调用它们,因为它们需要按特定顺序调用
为了实现这一点,每个模块中包含的宏都使用gccconstructor
属性
将init函数名添加到全局列表中
init函数体中包含的另一个宏更新全局列表以生成
请注意,函数实际上已被调用
最后,在所有初始化完成后,在main()
中调用check函数
注:
constructor
属性
Nick D演示了##预处理器技巧并给出了框架
tod frye提供了一种基于链接器的聪明方法,可用于许多编译器
其他所有人帮助他人并分享有用的花絮
nx\u lib\u 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); }
nxl\u func\u run\u 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;
}
为什么不编写一个后处理脚本来为您进行检查呢。然后将该脚本作为构建过程的一部分运行。。。或者更好的是,让它成为你的测试之一。您正在编写测试,对吗?:) 例如,如果每个模块都有一个头文件modX.c。如果init()函数的签名是“void init()” 让脚本grep遍历所有的.h文件,并创建需要初始化()的模块名称列表。然后让脚本检查main()中的每个模块是否确实调用了init()。Splint(可能还有其他Lint变体)可以对已定义但未调用的函数发出警告
有趣的是,大多数编译器都会警告您未使用的变量,而不是未使用的函数。我不知道下面的内容有多难看,但我还是发布了:-) (基本思想是注册函数指针,就像函数所做的一样。
当然,
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
...
}
。。。在其他模块中:
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
typedef int(*函数(无效);
外部int add2init(函数f);
#定义M_add2init(函数名称)静态int_35;#函数名称=add2init(函数名称)
int foo(无效)
{
printf(“foo\n”);
返回0;
}
M_add2init(foo);// 如果单个模块表示“类”实体并具有实例构造函数,则可以使用以下构造:
static inline void init(void) { ... }
static int initialized = 0;
#define INIT if (__predict_false(!initialized)) { init(); initialized = 1; }
struct Foo *
foo_create(void)
{
INIT;
...
}
其中“\uuuupredict\ufalse
”是编译器的分支预测提示。创建第一个对象时,模块将自动初始化(一次)。如果gcc适合您的项目,您可以使用的扩展名\uuuuuuuu属性((构造函数))
#include <stdio.h>
void func1() __attribute__((constructor));
void func2() __attribute__((constructor));
void func1()
{
printf("%s\n",__func__);
}
void func2()
{
printf("%s\n",__func__);
}
int main()
{
printf("main\n");
return 0;
}
//the output
func2
func1
main
#包括
void func1()_属性__((构造函数));
void func2()_属性__((构造函数));
void func1()
{
printf(“%s\n”,函数);
}
void func2()
{
printf(“%s\n”,函数);
}
int main()
{
printf(“主\n”);
返回0;
}
//输出
功能2
职能1
主要的
您可以使用链接器部分按照这些思路进行操作。无论何时定义init函数,都要在链接器部分中放置一个指向它的指针,仅用于init函数指针。然后,您至少可以知道编译了多少init函数
如果调用init函数的顺序不重要,并且所有函数都具有相同的原型,那么您可以从main在一个循环中调用它们
确切的细节我记不清了,但它是这样工作的:
在模块文件中
//this is the syntax in GCC..(or would be if the underscores came through in this text editor)
initFuncPtr thisInit __attribute((section(.myinits)))__= &moduleInit;
void moduleInit(void)
{
// so init here
}
这将在.myinits节中放置一个指向模块init函数的指针,但将代码保留在.code节中。因此.myinits部分只是指针。您可以将其视为模块文件可以添加到的可变长度数组
那你就可以进入门派了
//this is the syntax in GCC..(or would be if the underscores came through in this text editor)
initFuncPtr thisInit __attribute((section(.myinits)))__= &moduleInit;
void moduleInit(void)
{
// so init here
}
void foo_logic();
void bar_logic();
void foo() {
if (module_state == BEFORE_INIT) {
handle_not_initialized_error();
}
foo_logic();
}